2009-08-26 3 views
2

У меня есть конкретный класс, который содержит коллекцию другого конкретного класса. Я хотел бы, чтобы выставить обоих классов через интерфейсы, но у меня возникают проблемы, выяснить, как я могу выставить Collection <ConcreteType> член как Коллекция < интерфейса > член.Как вы можете вернуть коллекцию <ConcreteType> в виде коллекции <Interface>?

настоящее время я использую .NET 2.0

Код ниже приводит к ошибке компилятора:

Cannot implicitly convert type 'System.Collections.ObjectModel.Collection<Nail>' to 'System.Collections.ObjectModel.Collection<INail>'

Закомментированный попытка бросить дать эту ошибку компилятора:

Cannot convert type 'System.Collections.ObjectModel.Collection<Nail>' to
'System.Collections.ObjectModel.Collection<INail>' via a reference conversion, boxing conversion, unboxing conversion, wrapping conversion, or null type conversion.

Есть любой способ показать коллекцию конкретных типов в виде набора интерфейсов или мне нужно создать новую коллекцию в методе getter интерфейса?

using System.Collections.ObjectModel; 

public interface IBucket 
{ 
    Collection<INail> Nails 
    { 
     get; 
    } 
} 

public interface INail 
{ 
} 

internal sealed class Nail : INail 
{ 
} 

internal sealed class Bucket : IBucket 
{ 
    private Collection<Nail> nails; 

    Collection<INail> IBucket.Nails 
    { 
     get 
     { 
      //return (nails as Collection<INail>); 
      return nails; 
     } 
    } 

    public Bucket() 
    { 
     this.nails = new Collection<Nail>(); 
    } 
} 
+0

Вы можете как прочитать серию статей: http://blogs.msdn.com/ericlippert/archive/tags/Covariance+and+Contravariance/default .aspx –

ответ

-1

C# не поддерживает ковариацию общих коллекций (поддерживается только для массивов). В таких случаях я использую класс адаптера. Он просто перенаправляет все вызовы на фактический сбор, преобразовывая значения в требуемый тип (не требует копирования всех значений списка в новую коллекцию). Использование выглядит следующим образом:

Collection<INail> IBucket.Nails 
{ 
    get 
    { 
     return new ListAdapter<Nail, INail>(nails); 
    } 
} 

    // my implementation (it's incomplete) 
    public class ListAdapter<T_Src, T_Dst> : IList<T_Dst> 
{ 
    public ListAdapter(IList<T_Src> val) 
    { 
     _vals = val; 
    } 

    IList<T_Src> _vals; 

    protected static T_Src ConvertToSrc(T_Dst val) 
    { 
     return (T_Src)((object)val); 
    } 

    protected static T_Dst ConvertToDst(T_Src val) 
    { 
     return (T_Dst)((object)val); 
    } 

    public void Add(T_Dst item) 
    { 
     T_Src val = ConvertToSrc(item); 
     _vals.Add(val); 
    } 

    public void Clear() 
    { 
     _vals.Clear(); 
    } 

    public bool Contains(T_Dst item) 
    { 
     return _vals.Contains(ConvertToSrc(item)); 
    } 

    public void CopyTo(T_Dst[] array, int arrayIndex) 
    { 
     throw new NotImplementedException(); 
    } 

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

    public bool IsReadOnly 
    { 
     get { return _vals.IsReadOnly; } 
    } 

    public bool Remove(T_Dst item) 
    { 
     return _vals.Remove(ConvertToSrc(item)); 
    } 

    public IEnumerator<T_Dst> GetEnumerator() 
    { 
     foreach (T_Src cur in _vals) 
      yield return ConvertToDst(cur); 
    } 

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

    public override string ToString() 
    { 
     return string.Format("Count = {0}", _vals.Count); 
    } 

    public int IndexOf(T_Dst item) 
    { 
     return _vals.IndexOf(ConvertToSrc(item)); 
    } 

    public void Insert(int index, T_Dst item) 
    { 
     throw new NotImplementedException(); 
    } 

    public void RemoveAt(int index) 
    { 
     throw new NotImplementedException(); 
    } 

    public T_Dst this[int index] 
    { 
     get { return ConvertToDst(_vals[index]); } 
     set { _vals[index] = ConvertToSrc(value); } 
    } 
} 
+1

Такой вид выглядит излишним в этот момент .... – RCIX

+0

Это слишком много, если вам нужно только один раз, но этот класс облегчает жизнь когда вам нужна ковариация чаще. – skevar7

+0

Прохладный, я думаю, что это именно то, что мне нужно! – jameswelle

6

C# 3.0 generics являются инвариантными. Вы не можете этого сделать, не создавая новый объект. В C# 4.0 вводится безопасная ковариация/контравариантность, которая в любом случае ничего не изменит в отношении коллекций чтения/записи (ваш случай).

6

Просто определить ногти, как

Collection<INail> 
+2

Это решение, но если вы хотите только коллекцию объектов Nail, это проблема, потому что каждый объект, реализующий INail, может быть добавлен в коллекцию. –

+0

Я бы жил с этим до C# 4. Конечно, это зависит от оригинального плаката. – ChaosPandion

+2

Я должен был упомянуть, что эти классы сериализуются с использованием XmlSerializer, поэтому коллекция должна быть определена как Collection , а не Collection . – jameswelle

1

Почему бы не просто вернуть его в качестве интерфейса, просто все открытые методы в интерфейсе, таким образом, вы не имеете эту проблему, и, если вы решите чтобы вернуть другой тип Nail-класса, тогда он будет работать нормально.

0

Какую версию .Net вы используете?

Если вы используете .net 3.0+, вы можете добиться этого только с помощью System.Linq.

Отъезд this question, который решил это для меня.

+0

Я использую .Net 2.0 – jameswelle

-1

вы можете использовать расширение Cast

nails.Cast<INail>() 

Я не могу проверить это здесь, чтобы обеспечить более полный пример, как мы используем .NET 2.0 на работе (тиски тиски), но у меня есть аналогичный вопрос here

+0

Этот q тоже .NET 2.0. – nawfal

0

Существует одно решение, которое может быть не совсем то, о чем вы просите, но может быть приемлемой альтернативой - вместо этого используйте массивы.

internal sealed class Bucket : IBucket 
{ 
    private Nail[] nails; 

    INail[] IBucket.Nails 
    { 
     get { return this.nails; } 
    } 

    public Bucket() 
    { 
     this.nails = new Nail[100]; 
    } 
} 

(Если вы в конечном итоге делает что-то вроде этого, имейте в виду, это Guidelines Framework Design примечание: как правило, массивы не должны быть подвержены как свойства, так как они, как правило, копируются перед возвратом в вызывающую и копирования является дорогостоящая операция, чтобы сделать внутри невинное получение свойства)

+0

Noooooooo ... следует избегать в 99% случаев :) – nawfal

0

использовать это как тело вашей собственности добытчика:.

List<INail> tempNails = new List<INail>(); 
foreach (Nail nail in nails) 
{ 
    tempNails.Add(nail); 
} 
ReadOnlyCollection<INail> readOnlyTempNails = new ReadOnlyCollection<INail>(tempNails); 
return readOnlyTempNails; 

то есть чуть-чуть немного Hacky решения, но это не то, что вы хотите.

Отредактировано для возврата ReadOnlyCollection. Обязательно обновите свои типы в IBucket и Bucket.

+0

Я думаю, что это то, что мне нужно будет сделать, но я верну ReadOnlyCollection, поскольку добавление к возвращенной коллекции не повлияет на оригинал. – jameswelle

+0

Это совсем не взломано, довольно звучит, и лучше и проще, чем большинство ответов :) – nawfal

0

Вы можете добавить некоторые дженерики. Подходит лучше, сильнее связан.

public interface IBucket<T> where T : INail 
{ 
    Collection<T> Nails 
    { 
     get; 
    } 
} 

public interface INail 
{ 
} 

internal sealed class Nail : INail 
{ 
} 

internal sealed class Bucket : IBucket<Nail> 
{ 
    private Collection<Nail> nails; 

    Collection<Nail> IBucket<Nail>.Nails 
    { 
     get 
     { 
      return nails; //works 
     } 
    } 

    public Bucket() 
    { 
     this.nails = new Collection<Nail>(); 
    } 
} 

Таким образом Collection<Nail> вы возвращаетесь из Bucket класса может только когда-либо держать Nail с. Любой другой INail не будет входить в него. Это может быть или не быть лучше в зависимости от того, что вы хотите.


Только если вы хотите Collection<INail> (свойство интерфейса) вы вернетесь из Bucket провести другие INail с (чем Nail с), то вы можете попробовать ниже подход. Но есть проблема. С одной стороны, вы говорите, что вы хотите использовать private Collection<Nail> в Bucket классе и не Collection<INail>, потому что вы не хотите, чтобы случайно добавить другие INail с от Bucket класса в него, но с другой стороны, вам придется добавить другие INail с от внешней Bucket класс. Это невозможно. в том же экземпляре. Компилятор останавливает вас от случайного добавления INail в Collection<Nail>. Один из способов - вернуть другой экземпляр Collection<INail> из вашего Bucket класса из существующих Collection<Nail>. Это менее эффективно, но может быть семантикой, которой вы пользуетесь. Обратите внимание, что это концептуально отличается от указанного выше

internal sealed class Bucket : IBucket 
{ 
    private Collection<Nail> nails; 

    Collection<INail> IBucket<Nail>.Nails 
    { 
     get 
     { 
      List<INail> temp = new List<INail>(); 
      foreach (Nail nail in nails) 
       temp.Add(nail); 

      return new Collection<INail>(temp); 
     } 
    } 

    public Bucket() 
    { 
     this.nails = new Collection<Nail>(); 
    } 
} 
Смежные вопросы