【问题标题】:c# type of an object when using derived classc# 使用派生类时的对象类型
【发布时间】:2018-07-26 08:02:33
【问题描述】:

有以下代码:

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对象?

【问题讨论】:

  • 我很难理解你的意思和你想要什么
  • 是的,但是发送的参数是 TrxBase 类型的。我知道在这种情况下,参数将只有 TrxBase 类型的属性。那么它是如何知道我的对象有一个属性 Prop3 的,而该属性目前是不可见的呢?
  • 当你将Trx作为TrxBase传递时,它并不是将Trx转换为基类型,它仍然是派生类型。这就是 C# 的工作原理。
  • @MineR 所以在方法里面,参数其实有派生类型的所有属性,但是不可见?有没有办法在没有反射的情况下获得这些属性?
  • 我认为这是您需要查找的时间,继承、多态性和铸造.. 我很想回答,但我认为这是一个滑坡(投反对票),并且你真的必须承担自己的使命

标签: c# types type-inference


【解决方案1】:

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

所以仅仅因为您将t 传递给Print 并要求TrxBase,它不会 t 更改为TrxBase

如果在Print 内部,它测试并确定它是Trx,那么将其转换回该类型(隐藏在模式匹配语法中)并开始将其视为类型是完全有效的它确实是(虽然,当然,它可能是一种更源自Trx的类型。


额外阅读:Eric Lippert 的Representation and Identity


1如果您了解引用更改转换为您提供了一个 new 对象。这在上面的额外阅读中也有进一步的解释。

【讨论】:

    【解决方案2】:

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

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

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

    【讨论】:

    • 但是在这种情况下,pattern patching 是使用反射来查看我的对象有一个属性 Prop3,所以它可以表示为 Trx 类型?
    • 呃,不,它没有使用反射。
    • @BudaGavril,不,如果您是 C# 新手(我假设您是),这不是反映,请想象一下:您有 10 个内存块用于您最衍生的对象及其所有字段, 属性等。当您决定转换为更多的基对象时,从转换的那一刻起,您只能访问这 10 块内存中的一小部分(来自基类的那些)。因此,可访问性很简单。
    猜你喜欢
    • 2021-07-02
    • 2018-11-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多