我有一个泛型函数,它返回一个AnyListVM
子类的新实例,基本上是这样实现的:
public TListVM MakeListVM<TListVM>()
where TListVM : AnyListVM
{
TListVM listVM;
switch(typeof(TListVM).ToString())
{
case nameof(EventListVM):
listVM = new EventListVM();
// some more init stuff
break;
// some more similar cases
default:
throw new NotImplementedException();
}
return listVM;
}
这两个相关的类目前看起来像那样,没有任何有意义的实现:
public abstract class AnyListVM
{
}
public class EventListVM : AnyListVM
{
}
现在,Visual Studio强调了我的new EventListVM()
并唠叨它无法将EventListVM
隐式转换为TListVM
。
好的,所以我想我只是添加一个明确的演员:
listVM = (TListVM)new EventListVM();
但是不。现在,Visual Studio再次强调它并说它是一个多余的演员。提供的自动修复将是再次删除演员。无限循环。
这里出了什么问题,为什么我不允许明确或暗示地进行演员表?
答案 0 :(得分:2)
你的实施中有一个明显的不正确之处,其他人已经指出但是没有令人满意地解决。如果您打算实例化 TListVM
,那么您需要更改两个非常重要的部分。首先是新代码清单:
public TListVM MakeListVM<TListVM>()
where TListVM : AnyListVM, new()
{
TListVM listVM = new TListVM();
EventListVM evtList = listVM as EventListVM;
if (evtList != null)
{
// set evtList properties. You can't change
// the instantiation method.
}
// repeat for other constructs.
return listVM;
}
现在,要阐述一下。通用where子句需要指定您打算使用无参数构造函数创建TListVM
。为此,您需要将new()
指定为通用约束。
这极大地简化了您的实现,它只知道有一个名为TListVM
的东西,其基类为AnyListVM
,并且有一个没有参数的构造函数。不需要复杂的switch语句,也不需要使用Activator
。
无论何时处理泛型,都要直接使用泛型参数。
根据更多信息,switch语句仍然是错误的工具。泛型必然会限制您使用对象的方式。您无法更改构造函数,但可以在实例化对象后专门设置属性。
上面我更改了列表以显示如何直接设置这些属性。
如果你已经死定了不同的构造函数等,那么你将不得不以不同的方式处理它。您必须返回基类,不 TListVM
。
public AnyListVM MakeListVM<TListVM>()
where TListVM : AnyListVM
{
return MakeListVM(typeof(TListVM)) as TListVM;
}
private AnyListVM MakeListVM(Type listVM)
{
AnyListVM listVM;
switch(listVM.ToString())
{
case nameof(EventListVM):
listVM = new EventListVM();
// some more init stuff
break;
// some more similar cases
default:
throw new NotImplementedException();
}
return listVM;
}
通用帮助器方法允许您包装更通用的工厂方法,以便它具有您想要的签名,而不会导致编译错误。
答案 1 :(得分:1)
根据您的通用限制,您无法保证EventListVM
将转换为TListVM
,允许其通过任何继承的AnyListVM
类,可能会或可能会不是EventListVM
。例如,如果此方法的调用者执行此操作:
AnyListVM vm = MakeListVM<SomeOtherListVMConcrete>();
它会失败,但不应该。
我相信你真正想要的是将EventListVM
投射到AnyListVM
,即实际的基本类型而不是通用类型。
AnyListVM listVM = new EventListVM();
但是,如果您总是返回EventListVM
的实例,我会考虑一起删除通用子句并更新签名以使返回类型为{{ 1}}。
答案 2 :(得分:1)
为了使您的问题更加明显,请考虑一个基类和两个孩子:
public class Base { } //AnyListVM
public class Child1 : Base{ } //EventListVM
public class Child2 : Base{ } //OtherListVM
现在您的方法如下:
public T Get<T>() where T : Base
{
//code
T item = new Child1();
//more code
}
想象一下,我发送了Child2
,这在方法签名中完全有效。该方法的代码现在看起来像:
Child2 item = new Child1();
当然在编译时会无效。