尝试强制转换为泛型类型时出错:冗余强制转换

时间:2016-05-11 13:04:55

标签: c# generics casting

我有一个泛型函数,它返回一个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再次强调它并说它是一个多余的演员。提供的自动修复将是再次删除演员。无限循环。

这里出了什么问题,为什么我不允许明确或暗示地进行演员表?

3 个答案:

答案 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();

当然在编译时会无效。