How to get around lack of covariance with IReadOnlyDictionary?

后端 未结 5 897
庸人自扰
庸人自扰 2020-12-01 17:48

I\'m trying to expose a read-only dictionary that holds objects with a read-only interface. Internally, the dictionary is write-able, and so are the objects within (see belo

相关标签:
5条回答
  • 2020-12-01 18:23

    Another approach for a specific lack of covariance:

    A work around for a specific type of useful covariance on idictionary

    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;
            }   
        }
    
    0 讨论(0)
  • 2020-12-01 18:26

    You could write your own read-only wrapper for the dictionary, e.g.:

    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();
        }
    }
    
    0 讨论(0)
  • 2020-12-01 18:29

    Maybe this solutions works for you:

    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!";
        }
    }
    

    Hope this helps!

    Greets

    0 讨论(0)
  • 2020-12-01 18:31

    I would suggest that you might want to define your own covariant interfaces, and include covariant access methods as well as a method which will create a read-only wrapper object which implements either IDictionary or IReadonlyDictionary with the desired types. Simply ignore IEnumerable<KeyValuePair<TKey,TValue>> within your interface.

    Depending upon what you're doing, it may be helpful to define an IFetchByKey<out TValue> which is inherited by IFetchByKey<in TKey, out TValue>, with the former accepting queries for any type of object (given an object instance, a collection of Cat should be able to say whether it contains that instance, even if it's a type Dog or ToyotaPrius; the collection won't contain any instances of the latter types, and should be able to say so).

    0 讨论(0)
  • 2020-12-01 18:46

    Depending on your use case, you might be able to get away with exposing a 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; }
    }
    
    0 讨论(0)
提交回复
热议问题