#c# #.net #oop #generics
#c# #.net #ооп #дженерики
Вопрос:
Как следует из названия, я хотел бы сделать следующее, но компилятор не позволяет мне:
public class MyClass
{
private List<SomeSupertype> myList;
public MyClass<T> (ICollection<T> elements) where T : SomeSupertype
{
myList = new List<SomeSuperType> ();
foreach (SomeSupertype element in elements) {
myList.Add (element);
}
}
}
Почему-то кажется невозможным добавить универсальный параметр в конструктор класса, который сам по себе не является универсальным. Обратите внимание, что MyClass должен быть не-универсальным типом, потому что клиенту не нужно различать разные типы MyClass после его создания.
Кто-нибудь может помочь мне разобраться в этом? Возможно ли это вообще? Это, безусловно, есть в Java 🙂
Редактировать: я забыл упомянуть, что я использую .NET 3.5 и, следовательно, нет поддержки ковариантных обобщений.
Комментарии:
1. Если вы используете .NET 4, почему бы просто не использовать неродовой конструктор
IEnumerable<SomeSupertype>
?IEnumerable
1` является ковариантным, поэтомуIEnumerable<T>
преобразуется вIEnumerable<SomeSupertype>
любоеT : SomeSupertype
время.2. @Anton: С некоторыми изменениями и объяснениями это кажется достойным ответа
3. Да, ковариация решила бы проблему. Вероятно, мне следует упомянуть, что я использую .NET 3.5
Ответ №1:
Конструкторы в C # не могут быть универсальными (то же самое с событиями, свойствами, деструкторами, операторами). Вместо этого рассмотрите возможность создания статического универсального фабричного метода:
public class MyClass
{
private List<SomeSupertype> myList;
private MyClass(List<SomeSupertype> myList)
{
this.myList = myList;
}
public static MyClass Create<T>(ICollection<T> elements)
where T : SomeSupertype
{
var myList = new List<SomeSuperType>(elements);
foreach (SomeSupertype element in elements)
{
myList.Add(element);
}
return new MyClass(myList);
}
}
Кстати, даже в .NET 3.5 вы могли бы использовать LINQ, чтобы упростить создание списка:
var myList = elements.Cast<SomeSuperType>().ToList();
Ответ №2:
Вы могли бы использовать шаблон factory для решения вашей проблемы:
public class MyClass
{
private List<SomeSuperType> myList = new List<SomeSuperType>();
public static MyClass MyClassBuilder<T>(ICollection<T> elements) where T : SomeSuperType
{
var myClass = new MyClass();
foreach (SomeSuperType element in elements)
{
myClass.myList.Add(element);
}
return myClass;
}
protected MyClass()
{
}
}
Или вы могли бы создать два класса, например:
// Put all the methods here
public class MyClass
{
protected List<SomeSuperType> myList = new List<SomeSuperType>();
protected MyClass()
{
}
}
// This class will define only the generic constructor
public class MyClass<T> : MyClass where T : SomeSuperType
{
public MyClass(ICollection<T> elements)
{
foreach (SomeSuperType element in elements)
{
myList.Add(element);
}
}
}
(это игнорирует тот факт, что, как написано Anton
, в C # > = 4.0 вы могли бы просто принять IEnumerable<SomeSuperType>
и быть счастливым)
Ответ №3:
Вы не можете сделать это в C #. Это хорошо, поскольку нам не нужны конструкторы, которые не соответствуют классу. Но все же есть несколько способов, которыми вы можете достичь своей цели.
Самый простой способ (если вы используете C # 4) — использовать IEnumerable вместо ICollection, потому что IEnumerable поддерживает контравариантность.
public class MyClass
{
private List<SomeSupertype> myList;
public MyClass (IEnumerable<SomeSuperType> elements)
{
myList = new List<SomeSuperType> ();
foreach (SomeSupertype element in elements) {
myList.Add (element);
}
}
}
Теперь вы можете вызывать constructor, не думая о приведении коллекции производных типов к коллекции базовых типов.
List<SomeDerivedClass> col = new List<SomeDerivedType>();
MyClass obj1 = new MyClass(col)
Второе решение, вы могли бы просто вызвать свой конструктор, создав коллекцию с помощью LINQ.
MyClass obj1 = new MyClass(col.Cast<SomeSuperClass>());
Третье решение, вы могли бы создать статический универсальный метод для создания новых объектов вашего класса.
public static MyClass Construct<T>(ICollection<T> elements) where T : ClassA
{
return new MyClass(elements.Cast<ClassA>().ToArray());
}
Четвертое решение, вы могли бы создать универсальный класс, который является производным от вашего базового класса, как кто-то уже указал.