【问题标题】:having trouble with assignment compatibility?作业兼容性有问题?
【发布时间】:2014-05-19 01:26:30
【问题描述】:

我想要做的是使用一个调用来返回一个基类的 2 个扩展的集合。这是我所拥有的:

public interface IView
{
   int Id { get; set; }
}

然后我有一个实现这个接口的域模型

public class View: IView
{
   public int Id { get; set; }
}

然后我有一些继承自基类的类

public class ViewA: View
{ 
   public string PropertyA { get; set; }
}

public class ViewB: View
{
   public string PropertyB { get; set; }
}

从这里我有一个我试图包含两者的列表:

var views = new List<IView>();

并从我的基地创建一个视图,因为 ViewA 和 ViewB 都有 Ids

var view = new View();
view.Id = 1;

现在我正在尝试将其转换为 ViewA,以便我可以像这样设置 PropertyA:

var viewA = (ViewA)view;

但调试器告诉我我不能将“视图”转换为 ViewA(编译得很好)。

Unable to cast object of type 'View' to type 'ViewA'

我确定在我对接口的理解中遗漏了一些东西 -> 基类继承,但我认为您可以通过这种方式将“较小”类转换为“更多定义”类。阅读有关协变、逆变和分配兼容性的信息,我认为我应该能够做到这一点;显然不是..

我想要做的是返回这两种视图的集合(因此是列表),以便我可以在需要它们时将它们转换为更定义的视图,并在我只需要公共属性时将它们保留为基本视图.

我宁愿在 ViewA 和 ViewB 中没有重复的属性,我正在尝试做的事情是可能的还是我只是把这一切都弄错了?

【问题讨论】:

    标签: c# inheritance polymorphism


    【解决方案1】:

    我确定在我对接口的理解中遗漏了一些东西 -> 基类继承,但我认为您可以通过这种方式将“较小”类转换为“更多定义”类。阅读有关协变、逆变和分配兼容性的信息,我认为我应该能够做到这一点;显然不是..

    你把它弄反了(我认为)。您始终可以将派生类型的实例强制转换为基类型,但反之则不行。如果将实例强制转换为派生类型,则它在运行时必须是该类型的实例。

    看起来您正在尝试以某种方式从View 的实例创建ViewA 的实例。这不是投射的工作原理。

    这是一个可能有用的例子。如果您有视图列表:

    var views = new List<IView>();
    

    然后添加ViewA 的实例:

    views.Add(new ViewA());
    

    现在,编译器并不“知道”views[0]ViewA——毕竟它是IViews 的列表。在这种情况下,您比编译器了解更多,并且适合进行强制转换:

    var viewA = (ViewA)views[0];
    

    你还提到:

    我宁愿在 ViewA 和 ViewB 中没有重复的属性

    除非我遗漏了什么,否则您没有复制任何属性。 ViewAViewB 具有不同的属性,并从 View 类继承 Id 属性。

    换句话说,这看起来不错:

    var view = new ViewA();
    
    view.Id = 4;
    

    【讨论】:

    • 那么我将如何绕过复制 id 设置?假设我将这个演员表包含在某个属性的 switch 语句中,它告诉我要进行哪个演员表,所以我不必在两个地方都设置 Id。是否可以这样做,还是我只需要在两个地方都设置 Id?
    • @JoshMeiburg:我只是在答案的末尾添加了一些内容。 IdViewAViewBView 类继承。您不需要在ViewAViewB 中重新定义此属性。
    • 我遇到的问题是我将演员表包装在 2 种 switch 语句中,因为根据某些标准,视图可能是 ViewA 或 ViewB;但是,我试图避免在 switch 语句的两种情况下设置 Id 属性。这就是我的意思是“我不想在 ViewA 和 ViewB 中有重复的属性”(我认为这不是很清楚).. 这更像是我不想复制 Id 属性设置(当然还有更多)。
    • @JoshMeiburg:为什么不将Id 属性的设置完全移出switch 语句?无需演员即可使用。
    • 如果你在你的例子中使用List.Add always adds the item to the end of the list 这样做Count - 1 很好。如果你想要一些更简洁的东西,你可以使用 LINQ 的 .Last() 方法。
    【解决方案2】:

    ViewAViewB 的实例添加到列表中,而不是简单地添加View

    var views = new List<IView>();
    
    var view = new ViewA();  // add an instance of viewA
    views.Add(view);
    

    您可以直接访问一个项目(无需强制转换,感谢 Andrew) 以更改基类上的“Id”属性,因为该属性也在接口中定义:

    var view = (View)views[0];
    view.Id = 999;
    

    或测试类型以访问其他更具体的属性:

    var viewB = views[0] as ViewB;  // first element is a ViewA, so viewB is null
    
    if (viewB != null)
    {
        viewB.PropertyB = "someValue";  // doesn't run because viewB is null
    }
    
    var viewA = views[0] as ViewA;
    
    if (viewA != null)
    {
        viewA.PropertyA = "someOtherValue";  // runs - you have an instance of viewA
    }
    

    【讨论】:

    • Id 属性是在接口上定义的,因此您不需要在第二个 sn-p 中强制转换为 View
    【解决方案3】:

    不,您不能将“较小”类转换为“更多定义”类。 但是你可以将 viewA 和 viewB 强制转换为 View 的列表,这样你就可以在获取公共属性的时候了。

    【讨论】:

      【解决方案4】:

      你用来创建一个对象的类型是它应该永远是的类型——它永远不会改变。

      因此,您将实例创建为:

      var view = new View();
      

      因为它继承了IView,所以你可以引用它或View,但它的类型永远不会改变View

      因此,当您尝试将其转换为 ViewA 时,它会失败。它永远不会是ViewA,除非你是这样创建的。

      如果你这样做:

      var view = new ViewA();
      

      然后它可以被引用为IViewViewViewA,但它始终是ViewA 类型。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-08-28
        • 2014-02-12
        • 2011-10-04
        • 2013-03-28
        • 2019-10-03
        • 2018-06-02
        • 2012-03-28
        相关资源
        最近更新 更多