Использование базовых объектов в качестве параметров в универсальной функции
-
05-07-2019 - |
Вопрос
Я пытаюсь реализовать вспомогательный метод с использованием обобщений (C # / 3.5) У меня хорошая структура классов, с базовыми классами, такими как:
public class SomeNiceObject : ObjectBase
{
public string Field1{ get; set; }
}
public class CollectionBase<ObjectBase>()
{
public bool ReadAllFromDatabase();
}
public class SomeNiceObjectCollection : CollectionBase<SomeNiceObject>
{
}
И я хочу получить коллекцию, используя общий метод, например так:
public class DAL
{
public SomeNiceObjectCollection Read()
{
return ReadFromDB<SomeNiceObjectCollection>();
}
T ReadFromDB<T>() where T : CollectionBase<ObjectBase>, new()
{
T col = new T();
col.ReadAllFromDatabase();
return col;
}
}
Это не строит, с
Error 66 The type 'SomeNiceObjectCollection' cannot be used as type parameter 'T' in the generic type or method 'ReadFromDB<T>'. There is no implicit reference conversion from 'SomeNiceObjectCollection' to 'CollectionBase<ObjectBase>'.
Объект SomeNiceObjectCollection является коллекцией CollectionBase, а точнее CollectionBase. Так как я могу заставить это работать?
Решение
C # не поддерживает приведение между типами списков (ковариация).
Лучшим вариантом для поддержки этого шаблона будет введение интерфейса для метода ReadAllFromDatabase, чтобы вы не полагались на универсальную коллекцию:
public class SomeNiceObject : ObjectBase
{
public string Field1{ get; set; }
}
public interface IFromDatabase
{
bool ReadAllFromDatabase();
}
public class CollectionBase<ObjectBase>() : IFromDatabase
{
public bool ReadAllFromDatabase();
}
public class SomeNiceObjectCollection : CollectionBase<SomeNiceObject>
{
}
public class DAL
{
public SomeNiceObjectCollection Read()
{
return ReadFromDB<SomeNiceObjectCollection>();
}
T ReadFromDB<T>() where T : IFromDatabase, new()
{
T col = new T();
col.ReadAllFromDatabase();
return col;
}
}
Другие советы
В C # 3.0 это невозможно, но в C # и .NET 4.0 с ковариацией и контравариантностью это возможно.
Подумайте об этом: вы берете коллекцию, содержащую производный объект, и пытаетесь временно обработать ее как коллекцию базового объекта. Если бы это было разрешено, вы можете вставить в список базовые объекты, которые не относятся к производному объекту.
Вот пример:
List<String> l = new List<String>();
List<Object> o = l;
l.Add(10); // 10 will be boxed to an Object, but it is not a String!