30

我正在尝试公开一个只读字典,该字典包含具有只读接口的对象。在内部,字典是可写的,其中的对象也是可写的(参见下面的示例代码)。我的问题是 IReadOnlyDictionary 不支持协变转换,因为这里的问题中列出了原因。这意味着我不能将我的内部字典公开为只读字典。

所以我的问题是,有没有一种有效的方法可以将我的内部字典转换为 IReadOnlyDictionary,或者其他方式来处理这个问题?我能想到的选项是:

  1. 持有两个内部词典并保持同步。
  2. 访问属性时创建一个新字典并转换其中的所有对象。
  3. 在内部使用时将 IReadOnly 转换回 NotReadOnly。

1似乎很痛苦,2似乎效率很低。3 听起来是目前最有前途的,但仍然很难看。我还有其他选择吗?

public class ExposesReadOnly
{
    private Dictionary<int, NotReadOnly> InternalDict { get; set; }
    public IReadOnlyDictionary<int, IReadOnly> PublicList
    {
        get
        {
            // This doesn't work...
            return this.InternalDict;
        }
    }

    // This class can be modified internally, but I don't want
    // to expose this functionality.
    private class NotReadOnly : IReadOnly
    {
        public string Name { get; set; }
    }
}

public interface IReadOnly
{
    string Name { get; }
}
4

5 回答 5

23

您可以为字典编写自己的只读包装器,例如:

public class ReadOnlyDictionaryWrapper<TKey, TValue, TReadOnlyValue> : IReadOnlyDictionary<TKey, TReadOnlyValue> where TValue : TReadOnlyValue
{
    private IDictionary<TKey, TValue> _dictionary;

    public ReadOnlyDictionaryWrapper(IDictionary<TKey, TValue> dictionary)
    {
        if (dictionary == null) throw new ArgumentNullException("dictionary");
        _dictionary = dictionary;
    }
    public bool ContainsKey(TKey key) { return _dictionary.ContainsKey(key); }

    public IEnumerable<TKey> Keys { get { return _dictionary.Keys; } }

    public bool TryGetValue(TKey key, out TReadOnlyValue value)
    {
        TValue v;
        var result = _dictionary.TryGetValue(key, out v);
        value = v;
        return result;
    }

    public IEnumerable<TReadOnlyValue> Values { get { return _dictionary.Values.Cast<TReadOnlyValue>(); } }

    public TReadOnlyValue this[TKey key] { get { return _dictionary[key]; } }

    public int Count { get { return _dictionary.Count; } }

    public IEnumerator<KeyValuePair<TKey, TReadOnlyValue>> GetEnumerator()
    {
        return _dictionary
                    .Select(x => new KeyValuePair<TKey, TReadOnlyValue>(x.Key, x.Value))
                    .GetEnumerator();
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }
}
于 2012-11-28T10:29:08.753 回答
1

我建议您可能想要定义自己的协变接口,并包括协变访问方法以及创建只读包装器对象的方法,该包装器对象实现IDictionaryIReadonlyDictionary使用所需的类型。IEnumerable<KeyValuePair<TKey,TValue>>只需在您的界面中忽略。

根据您正在做的事情,定义IFetchByKey<out TValue>由 继承的可能会有所帮助IFetchByKey<in TKey, out TValue>,前者接受对任何类型对象的查询(给定对象实例, 的集合Cat应该能够说明它是否包含该实例,即使它是一个类型DogToyotaPrius; 集合将不包含后一种类型的任何实例,并且应该能够这样说)。

于 2012-12-03T17:51:47.097 回答
0

也许这个解决方案适合你:

public class ExposesReadOnly
{
    private IDictionary<int, IReadOnly> InternalDict { get; set; }
    public IReadOnlyDictionary<int, IReadOnly> PublicList
    {
        get
        {
            IReadOnlyDictionary<int, IReadOnly> dictionary = new ReadOnlyDictionary<int, IReadOnly>(InternalDict);

            return dictionary;
        }
    }

    private class NotReadOnly : IReadOnly
    {
        public string Name { get; set; }
    }

    public void AddSomeValue()
    {
        InternalDict = new Dictionary<int, NotReadOnly>();
        InternalDict.Add(1, new NotReadOnly() { Name = "SomeValue" });
    }
}

public interface IReadOnly
{
    string Name { get; }
}

class Program
{
    static void Main(string[] args)
    {
        ExposesReadOnly exposesReadOnly = new ExposesReadOnly();
        exposesReadOnly.AddSomeValue();

        Console.WriteLine(exposesReadOnly.PublicList[1].Name);
        Console.ReadLine();

        exposesReadOnly.PublicList[1].Name = "This is not possible!";
    }
}

希望这可以帮助!

问候

于 2012-11-27T22:28:30.190 回答
0

另一种特定缺乏协方差的方法:

一种针对特定类型的有用协方差的变通方法

public static class DictionaryExtensions
    {
        public static IReadOnlyDictionary<TKey, IEnumerable<TValue>> ToReadOnlyDictionary<TKey, TValue>(
            this IDictionary<TKey, List<TValue>> toWrap)
        {
            var intermediate = toWrap.ToDictionary(a => a.Key, a =>a.Value!=null? a.Value.ToArray().AsEnumerable():null);
            var wrapper = new ReadOnlyDictionary<TKey, IEnumerable<TValue>>(intermediate);
            return wrapper;
        }   
    }
于 2014-04-02T15:23:14.263 回答
0

根据您的用例,您可能能够摆脱暴露Func<int,IReadOnly>.

public class ExposesReadOnly
{
    private Dictionary<int, NotReadOnly> InternalDict { get; set; }
    public Func<int,IReadOnly> PublicDictionaryAccess
    {
        get
        {
            return (x)=>this.InternalDict[x];
        }
    }

    // This class can be modified internally, but I don't want
    // to expose this functionality.
    private class NotReadOnly : IReadOnly
    {
        public string Name { get; set; }
    }
}

public interface IReadOnly
{
    string Name { get; }
}
于 2017-11-15T04:39:59.937 回答