У меня есть класс как таковой
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
Удивительно, я заставил его работать, написав код как таковой:
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 );
не работает.
Кажется, есть разница в кастинге как
и ()
.
Можете ли вы перевернуть дизайн? Вместо создания негибкого фабричного метода, который должен знать о каждом подклассе OneType
, добавьте абстрактный метод в OneType
, который выглядит следующим образом:
public MyClass<OneType> GetMyClass();
TwoType становится ответственным за создание ] Объекты подкласса
, ThreeType
могут возвращать SubTypeThree
и т. Д.
Подсказка здесь в том, что вы выполняете переключение в зависимости от типа объекта; это всегда хороший кандидат на то, чтобы заставить подклассы выполнять работу.
РЕДАКТИРОВАТЬ 1: Пример
Например;
public class TwoType: MyClass<TwoType>
{
public override MyClass<OneType> GetMyClass()
{
return new SubClass(this);
}
}
Это должно сработать:
return (MyClass<T>)(object)new SubClass((TwoType)(object)vd);
Это работает на меня:
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: Очевидно, что это не идеально, как Эрик указал выше. В то время как этот код технически компилируется и работает в той степени, в которой вы можете захотеть найти лучшее общее решение.
Поскольку 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
}
Теперь все понятно? Вы можете рассмотреть совершенно другой подход; когда вам нужно так много каст и проверок типа выполнения, чтобы убедить компилятор, что код корректен, это свидетельствует о том, что все это плохо пахнет кодом.
Это не собирается работать в .NET 3.5, а ниже - подкласс не имеет значения MyClass
для любого t, это только тип MyClass
. И общие классы не следует наследию их шаблона, например, MyClass
[111088] Подкласс MyClass
- они совершенно разные классы в C #.
К сожалению, я не знаю никакого разумного способа написать ваш фабричный метод.
Измените свой фабричный метод:
public static MyClass<T> Factory<T>(T vd)
where T: OneType
{
return new MyClass<T>(vd);
}
Тогда вам вообще не нужен переключатель.
У вас нет намного намного на твоему методу утилиты.
public static MyClass<T> Factory<T>(T vd) where T: OneType
{
// ...
}