0

为什么我会收到以下代码的类型转换编译错误?

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

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

请注意,Def & View 类故意不派生自某个公共类。此外,我总是只想从 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
    }
}
4

2 回答 2

2

无法保证转换Tdef. 肯定有到 的转换BaseDef,编译器使用该转换:

BaseDef df = vm; // This is fine

……但这不是一回事。

在这种情况下,该转换实际上将返回 aBaseDef无论如何 -没有转换运算符 from DerivedViewto DerivedDef... 有一个方法 ( ToDerivedDef) 但您的代码中没有任何内容可以调用它。即使在这种特殊情况下存在转换,编译器也不能保证它存在。

你可以使用:

Tdef df = (Tdef) (BaseDef) vm;

...这将执行用户定义的转换为BaseDef,然后正常转换为Tdef- 在您的情况下,这将在执行时失败,但如果转换调用适当的虚拟方法,则可以工作。但是在编译时不能保证。

于 2013-06-05T15:10:47.077 回答
0

我真的不能使用 Jon 的方法,因为我有分层限制。def 模型在 DB 层中定义,而视图模型在 UI 层中定义。

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

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);
    }
于 2013-06-06T06:59:12.980 回答