Почему случается так, что ограничение дженериков не может быть литым к своему производному типу?

Это является довольно озадачивающим, чтобы узнать, что Ограничение Дженериков не Может Быть Литым к Своему Производному типу.

Скажем, у меня есть следующий код:

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);
    }

Кажется мне довольно ужасным. Почему это так?

8
задан Graviton 17 May 2010 в 07:35
поделиться

4 ответа

Прежде всего, в вашем универсальном методе тип 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.
}
0
ответ дан 5 December 2019 в 15:20
поделиться

Во-первых, вы не должны приводить переменную базового типа к производному типу. Это не должно работать, только наоборот.

Во-вторых, он работает через объект , потому что вы удаляете проверки типов во время компиляции. Компилятор может проверить, что BaseType не может быть приведен к DerivedType . Но когда переменной является объект , компилятор оставляет ее, предполагая, что вы знаете, что делаете. Даже если он будет скомпилирован, код во время выполнения выйдет из строя.

7
ответ дан 5 December 2019 в 15:20
поделиться

Ответ прост: компилятор не может знать, что T в вашем методе FreeConversion можно преобразовать в DeriveClass .

3
ответ дан 5 December 2019 в 15:20
поделиться

Как вы уже сказали, дешевый трюк заключается в том, чтобы сначала кастовать к объекту, а затем к типу, к которому вы хотите перейти. Уродливо, но работает.

Кроме того, возможно, вы нарушаете принцип замещения Лискова, который не причинит вреда животным, но может привести ваш дизайн к не поддерживаемому коду.

В-третьих, хороший трюк, позволяющий вашему базовому классу раскрывать производный тип, выглядит примерно так:

public class Base<T> where T : Base<T> {
  T IAmDerived;
}

public class Derived : Base<Derived> { }
1
ответ дан 5 December 2019 в 15:20
поделиться
Другие вопросы по тегам:

Похожие вопросы: