2015-12-18 2 views
4

Когда у меня есть переменная ICollection<T> в C#, я не могу передать его в функцию, которая ожидает IReadOnlyCollection<T>:Как превратить ICollection <T> в IReadOnlyCollection <T>?

public void Foo() 
{ 
    ICollection<int> data = new List<int>(); 
    // Bar(data); // Not allowed: Cannot implicitly cast ICollection<int> to IReadOnlyCollection<int> 
    Bar(data.ToList()); // Works, since List<T> implements IReadOnlyCollection<T> 
} 

public void Bar(IReadOnlyCollection<int> data) 
{ 
    if (data.Count == 1) { /* ... */ } 
    // ... 
} 

Видимо, проблема в том, что ICollection<T> не наследует от IReadOnlyCollection<T> - но почему? ICollection<T> должен быть полным функциональным набором IReadOnlyCollection<T> плюс функции, которые изменяют коллекцию.

И что является лучшим решением для передачи аргументов?

С одной стороны, поскольку я не хочу изменять коллекцию в Bar, и вам просто нужно подсчитать и перебрать коллекцию, я бы хотел потребовать IReadOnlyCollection.

С другой стороны, я не хочу создавать новый объект списка каждый раз, когда я вызываю эту функцию.

+0

Что именно вы делаете с коллекцией в этом методе? Просто проверить счет? Вероятно, есть лучший способ справиться с этой ситуацией. –

+0

Вы действительно используете ReadOnlyCollection, когда не хотите, чтобы что-то вне вашего класса изменяло вашу коллекцию. Если это не является публичной собственностью, проблема становится тривиальной. – Xcalibur37

+0

@ M.kazemAkhgary Это просто перерыв во время выполнения, если кто-то передает «ICollection», который не является «IReadOnlyCollection». – Servy

ответ

3

Там нет стандартного решения AFAIK, но это не трудно сделать свой собственный, как этот

public static class MyExtensions 
{ 
    public static IReadOnlyCollection<T> AsReadOnly<T>(this ICollection<T> source) 
    { 
     if (source == null) throw new ArgumentNullException("source"); 
     return source as IReadOnlyCollection<T> ?? new ReadOnlyCollectionAdapter<T>(source); 
    } 
    sealed class ReadOnlyCollectionAdapter<T> : IReadOnlyCollection<T> 
    { 
     ICollection<T> source; 
     public ReadOnlyCollectionAdapter(ICollection<T> source) { this.source = source; } 
     public int Count { get { return source.Count; } } 
     public IEnumerator<T> GetEnumerator() { return source.GetEnumerator(); } 
     IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } 
    } 
} 

И затем использовать его следующим образом

Bar(data.AsReadOnly()); 
+2

Вы бы подумали, что это будет частью стандарта. –

5

Вы можете довольно тривиальным создать класс, который компонует a ICollection<T> при осуществлении IReadOnlyCollection<T>. Вы также можете создать метод расширения для выполнения обертывания (и, таким образом, разрешить общий тип вывода):

public class ReadOnlyCollectionWrapper<T> : IReadOnlyCollection<T> 
{ 
    private ICollection<T> collection; 
    public ReadOnlyCollectionWrapper(ICollection<T> collection) 
    { 
     this.collection = collection; 
    } 

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

    public IEnumerator<T> GetEnumerator() 
    { 
     return collection.GetEnumerator(); 
    } 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return collection.GetEnumerator(); 
    } 
} 

public static class ReadOnlyCollectionWrapper 
{ 
    public static IReadOnlyCollection<T> AsReadOnly<T>(this ICollection<T> collection) 
    { 
     return new ReadOnlyCollectionWrapper<T>(collection); 
    } 
} 
+0

Спасибо! Иван Стоев предложил то же самое ниже. Я принял его ответ, так как он был на 2 минуты быстрее. Надеюсь, вы понимаете. – LWChris

Смежные вопросы