使用派生类时对象的C#类型

时间:2018-07-26 08:02:33

标签: c# types type-inference

具有以下代码:

class TrxBase
{
    public string Prop1 { get; set; }
    public string Prop2 { get; set; }
}
class Trx : TrxBase
{
    public string Prop3 { get; set; }
}

    static void Print(TrxBase trx)
    {
        if (trx is Trx trx1)
        {
            Console.WriteLine(trx1.Prop3);
        }
        else
        {
            Console.WriteLine("no match");
        }
    }

    static void Main(string[] args)
    {
        Trx t = new Trx();
        t.Prop1 = "prop 1";
        t.Prop3 = "prop 3";

        Print(t);
    }

上面的代码显示“ prop 3”。据我所知。在Print方法中,该对象将作为TrxBase读取。如果是这种情况,Prop3属性保存在哪里?程序如何知道我的参数实际上是Trx对象?

2 个答案:

答案 0 :(得分:2)

您需要区分编译时间类型(例如,确定要调用的方法的重载类型)和运行时类型(例如,反射使用)。无论您对特定对象 1 进行什么扭曲(将其转换为基本类型,等等)都不会更改该对象的运行时类型。

因此,仅因为您要将t传递给要求Print的{​​{1}},它并不会 change TrxBase变成t

如果在TrxBase中测试并确定它是Print,则将其强制转换回该类型(隐藏在模式匹配语法中)并开始处理是完全有效的它确实是一种类型(当然,可以可以是从Trx派生的类型。


奖金阅读:埃里克·利珀特(Eric Lippert)的Representation and Identity


1 只要您了解引用转换会为您提供 new 对象。上面的奖金阅读中也对此做了进一步解释。

答案 1 :(得分:1)

这是C#的工作原理,当您将派生类型对象传递给具有基本类型对象参数的方法时,编译器只需获取此派生对象并将其解释为基本对象即可。

在您的情况下,您要将派生(Trx)对象传递给具有TrxBase参数的方法。因此,现在在Print(TrxBase trx)的范围内,trx将被视为TrxBase,但是随后您使用pattern matching来确定此trx是否可以表示为更多派生的Trx对象类型,在您的情况下为true,可以并且因此打印prop 3

可以将派生类型转换为更多基本类型,但是另一种方法将导致InvalidCastException来自CLR。因为如果您考虑一下-假设您分配类型为TrxBase的新对象,则CLR分配器将在堆(如果值为值类型,则为堆栈)上分配具有该对象具有的所有所需属性的对象。现在,如果您从CLR请求将该特定对象转换为更特定的对象,那么您最终将要求将该特定内存布局更改为CLR不支持的另一种布局(从您的特定对象添加字段,属性等)。