Это является довольно озадачивающим, чтобы узнать, что Ограничение Дженериков не Может Быть Литым к Своему Производному типу.
Скажем, у меня есть следующий код:
public abstract class BaseClass
{
public int Version
{ get { return 1; } }
public string FixString { get; set; }
public BaseClass()
{
FixString = "hello";
}
public virtual int GetBaseVersion()
{
return Version;
}
}
public class DeriveClass: BaseClass
{
public new int Version
{ get { return 2; } }
}
И угадайте то, что, этот метод возвратит ошибку компиляции:
public void FreeConversion<T>(T baseClass)
{
if(baseClass.GetType()==typeof(DeriveClass)
var derivedMe = (DeriveClass)baseClass;
}
Я должен был бы бросить baseClass
кому: object
сначала, прежде чем я могу бросить его к DerivedClass
, т.е.
public void FreeConversion<T>(T baseClass)
{
if(baseClass.GetType()==typeof(DeriveClass)
var derivedMe = (DeriveClass)((object)baseClass);
}
Кажется мне довольно ужасным. Почему это так?
Прежде всего, в вашем универсальном методе тип T может быть типом значения или ссылочным типом. И причина, по которой это позволяет вам делать через «Object», заключается в том, что вы просто выполняете распаковку-бокс, которая работает для любого типа в системе.
Во-вторых, будет ужасной идеей преобразовать / преобразовать объект базового класса в производный класс. Вы нарушаете механику ООП.
Если вы действительно хотите вернуть объект типа, производного от базового класса, есть один способ - решение во многом похоже на то, что предложил Фрэнк.
//This is how you create a function in BaseClass that returns the collection
//of DerivedTypes when called from an object of type derivedclass, no magic just Generics.
//**The BaseClass**
public class BaseClass<T>
where T : BaseClass<T>
{
public HashSet<T> GetHashSet()
{
HashSet<T> _hSet = new HashSet<T>();
//do some work
//create a HashSet<T> and return;
return _hSet;
}
}
//**The Derived Class**
public class DerivedClass : BaseClass<DerivedClass>
{
//you have the method inherited.
}
Во-первых, вы не должны приводить переменную базового типа к производному типу. Это не должно работать, только наоборот.
Во-вторых, он работает через объект
, потому что вы удаляете проверки типов во время компиляции. Компилятор может проверить, что BaseType
не может быть приведен к DerivedType
. Но когда переменной является объект
, компилятор оставляет ее, предполагая, что вы знаете, что делаете. Даже если он будет скомпилирован, код во время выполнения выйдет из строя.
Ответ прост: компилятор не может знать, что T
в вашем методе FreeConversion
можно преобразовать в DeriveClass
.
Как вы уже сказали, дешевый трюк заключается в том, чтобы сначала кастовать к объекту, а затем к типу, к которому вы хотите перейти. Уродливо, но работает.
Кроме того, возможно, вы нарушаете принцип замещения Лискова, который не причинит вреда животным, но может привести ваш дизайн к не поддерживаемому коду.
В-третьих, хороший трюк, позволяющий вашему базовому классу раскрывать производный тип, выглядит примерно так:
public class Base<T> where T : Base<T> {
T IAmDerived;
}
public class Derived : Base<Derived> { }