使用Generic Helper类进行隐式转换

时间:2013-06-05 12:14:33

标签: c# generics covariance

为什么我会为以下代码获取类型转换编译错误?

我的项目中有很多派生Def / View类的实例。所有这些都有一些代码库,比如持久性,检索等。我想通过使用泛型编写一个帮助器类,我可以实现这个公共代码库的可维护性。

但是,在将视图分配给def的行上,DoSomeStuff方法中出现“类型转换”编译错误。我已经注意为所有基类和派生类编写隐式转换转换。

注意Def&故意查看类不是从某些常见类派生的。另外,我总是希望仅从View转换为Def,而不是反过来,因此只有我的View类具有在它们上定义的隐式转换。

我确实试着跟随Eric Lipert关于协方差和反演的讨论,但是随着他的例子的进展,我的脑子里充满了混乱。非常感谢任何有关此问题的帮助。

public class BaseDef
{
    public int Id { get; set; }
}

public class DerivedDef : BaseDef
{
    public string Name { get; set; }

    public DerivedDef()
        : base()
    {

    }

    public DerivedDef(BaseDef bd)
    {
        this.Id = bd.Id;
    }
}

public class BaseView
{
    public int Id { get; set; }

    public BaseView()
    {

    }

    public BaseView(BaseDef bd)
    {
        Id = bd.Id;
    }

    public BaseDef ToBaseDef()
    {
        return new BaseDef { Id = this.Id };
    }

    public static implicit operator BaseView(BaseDef bd)
    {
        return new BaseView(bd);
    }

    public static implicit operator BaseDef(BaseView bv)
    {
        return bv.ToBaseDef();
    }
}

public class DerivedView : BaseView
{
    public string Name { get; set; }

    public DerivedView()
        : base()
    {

    }

    public DerivedView(DerivedDef dd)
        : base(dd)
    {
        Name = this.Name;
    }

    public DerivedDef ToDerivedDef()
    {
        return new DerivedDef(this)
        {
            Name = this.Name,
        };
    }
}

public class SomeHelper<Tdef, Tview>
    where Tdef : BaseDef
    where Tview : BaseView
{
    public void DoSomeStuff(Tview vm)
    {
        Tdef df = vm;   // this line give a compile error 'Cannot convert type 'Tview' to 'Tdef'
        // work with df from here on
    }
}

2 个答案:

答案 0 :(得分:2)

无法保证 转换为Tdef。肯定会转换为BaseDef,编译器使用该转换:

BaseDef df = vm; // This is fine

......但那不是一回事。

在这种情况下,转换实际上会返回BaseDef - 没有转化运算符从DerivedViewDerivedDef ...方法(ToDerivedDef),但代码中没有任何内容可以调用它。即使在这种特殊情况下存在转换,编译器也不能保证它存在。

您可以使用:

Tdef df = (Tdef) (BaseDef) vm;

...将执行用户定义的转换为BaseDef,然后执行正常转换为Tdef - 在您的情况下执行时会失败,但如果转换调用则可能有效一个合适的虚拟方法。但是在编译时它不能保证

答案 1 :(得分:0)

我真的无法使用Jon的方法,因为我有分层约束。 def模型在DB层中定义,而在UI层中定义视图模型。

然而,从Jon的评论中汲取灵感,我如何解决手头的问题是在所有视图模型上添加隐式转换,并在辅助类上公开处理转换为&amp;的两个属性。来回。这是最终代码的外观......

public class BaseDef
{
    public int Id { get; set; }

    public override string ToString()
    {
        return Id.ToString();
    }
}

public class BaseView
{
    public int Id { get; set; }

    public BaseView()
    {

    }

    public BaseView(BaseDef bd)
    {
        Id = bd.Id;
    }

    public BaseDef ToBaseDef()
    {
        return new BaseDef { Id = this.Id };
    }

    public static implicit operator BaseView(BaseDef bd)
    {
        return new BaseView(bd);
    }

    public static implicit operator BaseDef(BaseView bv)
    {
        return bv.ToBaseDef();
    }
}

public class DerivedDef : BaseDef
{
    public string Name { get; set; }

    public DerivedDef()
        : base()
    {

    }

    public DerivedDef(BaseDef bd)
    {
        this.Id = bd.Id;
    }
}

public class DerivedView : BaseView
{
    public string Name { get; set; }

    public DerivedView()
        : base()
    {

    }

    public DerivedView(DerivedDef dd)
        : base(dd)
    {
        Name = this.Name;
    }

    public DerivedDef ToDerivedDef()
    {
        return new DerivedDef((BaseView)this)
        {
            Name = this.Name,
        };
    }

    public static implicit operator DerivedView(DerivedDef dd)
    {
        return new DerivedView(dd);
    }

    public static implicit operator DerivedDef(DerivedView dv)
    {
        return dv.ToDerivedDef();
    }
}

public class SomeHelper<Tdef, Tview>
    where Tdef : BaseDef
    where Tview : BaseView
{
    public Func<Tview, Tdef> ConvertToDef { get; set; }
    public Func<Tdef, Tview> ConvertToView { get; set; }
    public Tdef Convert(Tview vm)
    {
        if (ConvertToDef == null)
        {
            throw new ArgumentNullException("ConvertToDef uninitialized");
        }
        return ConvertToDef(vm);
    }

    public Tview Convert(Tdef dm)
    {
        if (ConvertToView == null)
        {
            throw new ArgumentNullException("ConvertToView uninitialized");
        }
        return ConvertToView(dm);
    }
}
消费代码看起来像这样......

    private static void TestCastWithGenerics()
    {
        BaseDef bd = new BaseDef()
        {
            Id = 1
        };

        DerivedView dv = new DerivedView()
        {
            Id = 1,
            Name = "DV",
        };
        var aClassD = new SomeHelper<DerivedDef, DerivedView>();
        aClassD.ConvertToDef = dv1 => dv1; // Behind scenes the implicit cast is being invoked...
        DerivedDef dd = aClassD.Convert(dv);

        var aClassB = new SomeHelper<BaseDef, BaseView>();
        aClassB.ConvertToView = bd1 => bd1; // Behind scenes the implicit cast is being invoked...
        BaseView bv = aClassB.Convert(bd);

        Console.WriteLine(dd);
        Console.WriteLine(bv);
    }