Бросок от дженериков <T> к определенному SubClass

У меня есть класс как таковой

public class MyClass<T> where T : OneType
{
   T MyObj { get; set; }

   public MyCLass(T obj)
   {
   }
}

public class SubClass: MyClass<TwoType>
{
}

// snip for other similar class definition

где, TwoType получен из OneType.

Теперь, у меня есть этот служебный метод

public static MyClass<T> Factory<T>(T vd)
 where T : OneType
{
   switch(vd.TypeName)
   {
      case Constant.TwoType
          return new SubClass((TwoType)vd);
     // snip for other type check
   }
}

То, которое функция, очевидно, проверяет тип vd, и создавание соответствующего MyClass ввести. Единственной проблемой является вышеупомянутый код, не скомпилирует, и я не знаю почему

Ошибка

Не может выражение приведения типа T к TwoType

8
задан anar khalilov 2 June 2015 в 08:52
поделиться

8 ответов

Удивительно, я заставил его работать, написав код как таковой:

return (new SubClass(vd as TwoType) as MyClass<T>);

или

return (MyClass<T>)(object)new SubClass((TwoType)(object)vd );

Но,

return (MyClass<T>)new SubClass((TwoType)vd );

не работает.

Кажется, есть разница в кастинге как и ().

1
ответ дан 5 December 2019 в 07:58
поделиться

Можете ли вы перевернуть дизайн? Вместо создания негибкого фабричного метода, который должен знать о каждом подклассе OneType , добавьте абстрактный метод в OneType , который выглядит следующим образом:

public MyClass<OneType> GetMyClass();

TwoType становится ответственным за создание ] Объекты подкласса , ThreeType могут возвращать SubTypeThree и т. Д.

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

РЕДАКТИРОВАТЬ 1: Пример

Например;

public class TwoType: MyClass<TwoType>
{
  public override MyClass<OneType> GetMyClass()
  {
      return new SubClass(this);
  }
}
0
ответ дан 5 December 2019 в 07:58
поделиться

Это должно сработать:

return (MyClass<T>)(object)new SubClass((TwoType)(object)vd);
0
ответ дан 5 December 2019 в 07:58
поделиться

Это работает на меня:

public class OneType
{

}

public class MyClass<T> where T : OneType
{
    T MyObj
    { get; set; }
    public MyClass(T obj)
    {
    }
    public static MyClass<T> Factory<T>(T vd)
      where T : OneType
    {
        if (vd is TwoType)
        {
            return (MyClass<T>)(object)new SubClass(vd as TwoType);
        }
        return null;
    }

    public string Working
    {
        get { return this.GetType().Name; }
    }

}

public class TwoType : OneType
{

}


public class SubClass : MyClass<TwoType>
{
    public SubClass(TwoType obj)
        : base(obj)
    {

    }
}

в моей форме у меня было вот что:

        MyClass<TwoType> t = MyClass<TwoType>.Factory<TwoType>(new TwoType());

        MessageBox.Show(t.Working);

EDIT: Очевидно, что это не идеально, как Эрик указал выше. В то время как этот код технически компилируется и работает в той степени, в которой вы можете захотеть найти лучшее общее решение.

0
ответ дан 5 December 2019 в 07:58
поделиться

Поскольку Grzenio корректно отмечает, выражение типа T не конвертируется в TwoType. Компилятор не знает, что выражение гарантировано типом TwoType -- что гарантировано Вашим оператором "if", но компилятор не учитывает импликацию оператора if при анализе типов. Скорее, компилятор предполагает, что T может быть любым типом, удовлетворяющим ограничению, включая ThreeType, тип, производный от OneType, но не TwoType. Очевидно, что никакого преобразования из ThreeType в TwoType не происходит, поэтому и преобразования из T в TwoType тоже не происходит.

Можно обмануть компилятор, разрешив это, сказав "ну, относитесь к T как к объекту, а затем приведем объект к TwoType". Каждый шаг на этом пути легален -- T конвертируется в объект, и может произойти преобразование из объекта в TwoType, поэтому компилятор разрешает это.

Затем вы получите ту же самую проблему преобразования из SubClass в MyClass. Опять же, вы можете решить проблему, сначала приведя к объекту.

Однако, этот код все еще неверен:

public static MyClass<T> Factory<T>(T vd) 
 where T:OneType 
{ 
   switch(vd.TypeName) 
   { 
      case Constant.TwoType 
       // WRONG
       return (MyClass<T>)(object)(new SubClass((TwoType)(object)vd)); 
     // snip for other type check 
   } 
} 

Почему он неверен? Ну, подумайте обо всем, что здесь может пойти не так. Например:

class AnotherTwoType : TwoType { }
...
x = Factory<TwoType>(new AnotherTwoType());

Что происходит? Конструктор SubClass мы вызываем не потому, что аргумент не совсем типа TwoType, а типа, производного от TwoType. Вместо переключателя вы, вероятно, хотите

public static MyClass<T> Factory<T>(T vd) 
  where T:OneType 
{ 
  if (vd is TwoType)
       // STILL WRONG
       return (MyClass<T>)(object)(new SubClass((TwoType)(object)vd)); 
  // snip for other type check 
} 

Это все еще неправильно. Опять же, подумайте о том, что может пойти не так:

x = Factory<OneType>(new TwoType());

Теперь что происходит? аргумент - TwoType, мы делаем новый SubClass, а затем пытаемся преобразовать его в MyClass, но преобразование из SubClass в MyClass не происходит, так что это приведет к аварийному завершению работы и падению во время выполнения.

Правильный код для вашей фабрики -

public static MyClass<T> Factory<T>(T vd) 
  where T:OneType 
{ 
  if (vd is TwoType && typeof(T) == typeof(TwoType))
       return (MyClass<T>)(object)(new SubClass((TwoType)(object)vd)); 
  // snip for other type check 
} 

Теперь все понятно? Вы можете рассмотреть совершенно другой подход; когда вам нужно так много каст и проверок типа выполнения, чтобы убедить компилятор, что код корректен, это свидетельствует о том, что все это плохо пахнет кодом.

18
ответ дан 5 December 2019 в 07:58
поделиться

Это не собирается работать в .NET 3.5, а ниже - подкласс не имеет значения MyClass для любого t, это только тип MyClass . И общие классы не следует наследию их шаблона, например, MyClass [111088] Подкласс MyClass - они совершенно разные классы в C #.

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

1
ответ дан 5 December 2019 в 07:58
поделиться

Измените свой фабричный метод:

public static MyClass<T> Factory<T>(T vd)
    where T: OneType
{
    return new MyClass<T>(vd);
}

Тогда вам вообще не нужен переключатель.

0
ответ дан 5 December 2019 в 07:58
поделиться

У вас нет намного намного на твоему методу утилиты.

public static MyClass<T> Factory<T>(T vd) where T: OneType
{
    // ...
}
0
ответ дан 5 December 2019 в 07:58
поделиться
Другие вопросы по тегам:

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