【问题标题】:Why GetType() of a class "sliced" by interface tell me its a class type that isn't?为什么接口“切片”的类的 GetType() 告诉我它不是一个类类型?
【发布时间】:2019-07-22 12:32:48
【问题描述】:

Here's我的代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;

namespace Rextester
{
    public class Program
    {
        public static void Main(string[] args)
        {
            ISampleInterface a = new A();
            ISampleInterface b = new B();

            a.SampleMethod();
            b.SampleMethod();

            //Console.WriteLine(a.myValue); // can't do it!! a is not A
            Console.WriteLine(a.GetType()); // uhm...
        }
    }

    interface ISampleInterface
    {
        void SampleMethod();
    }

    class A : ISampleInterface
    {
        public double myValue = 10.0;

        public void SampleMethod() {
            Console.WriteLine("A");
        }
    }

    class B : ISampleInterface
    {
        public double myValue = 20.0;

        public void SampleMethod() {
            Console.WriteLine("B");
        }
    }
}

我通过接口初始化一个类(由类实现)。

显然,无法访问a.myValue,因为正确地Rextester.ISampleInterface 不包含这样的定义。

但如果我询问编译器哪个类型是a,它会输出Rextester.A(我相信不是)。

为什么?更重要的是,a 是哪一类?一种受其接口限制的混合切片类?不知道我会如何定义它......

【问题讨论】:

  • 无论你把它施放成什么,实例都是一样的。
  • 运行时 != 编译时间
  • 您似乎已停止对答案发表评论 - 目前尚不清楚您是否仍然不确定问题,或者您只是在等待决定是否接受。如果您仍然不清楚某些事情,如果我们知道什么会有所帮助。

标签: c# class types interface


【解决方案1】:

您可以调用哪些方法(外部反射/dynamic)取决于变量编译时类型

引用强制转换(将现有引用强制转换为它实现的接口)根本不会改变对象的类型。但是,如果您将该转换的结果存储在一个变量中,那么变量的类型就很重要。

我能想到的 .NET 世界中没有任何东西可以与 C++ 的切片(我假设您指的是切片)相媲美 - 当然,您不会通过编写简单的代码来获得它。

声明为接口类型的变量永远不会包含对“只是”该接口的东西的引用。它始终是某个特定的具体结构/类类型的对象。

【讨论】:

  • 你是说在内存中,它会为A分配空间?就算有一部分无法访问?
  • 你调用了new A()——这总是会产生一个A大小的对象。此后,只有一个对象可以被多个引用引用。并且您可以通过转回A 来访问其他成员。
  • @markzzz 请记住,在 C# 中,类和接口默认是 引用类型,这意味着变量 a 是对象的 引用在内存中,而不是对象本身,因此它可以引用AB。它类似于 C++ 中的ISampleInterface*
  • 我会说ISampleInterface& :) 是的,我主要来自 C++,我几乎不遵循这个... :)
  • 不,我的意思是ISampleInterface*。它是一个对象的reference(C++ 中的“指针”)。
【解决方案2】:

a.myValue 无法访问,因为它在ISampleInterface 的范围内

a.GetType() 正确地导致A,因为它在运行时被解析为分配给a 的类型的实例,即A。它仅在ISampleInterface 的范围内。

例如...

ISampleInterface a = new A();

您正在创建一个声明为ISampleInterfaceA实例

a.SampleMethod();

您可以访问声明的内容。

Console.WriteLine(a.myValue);

您无法访问声明的内容。

Console.WriteLine(a.GetType());

GetType 可以从 System.Object 调用,一切都自然扩展,所以你可以调用它,因为它是从隐式基类型声明的。在这种情况下,GetType 返回A,因为这正是a 的含义; A 的一个实例。

【讨论】:

  • 鉴于 C# 中 scope 的现有含义,我认为在这里使用 scope 而不是 type 非常令人困惑。
【解决方案3】:

但是如果我询问编译器哪个类型是a,它会输出Rextester.A(我相信不是)

如果“询问编译器”是指调用a.GetType(),那不是你在做的事情。您正在向 runtime 询问 a 引用的对象的 actual 类型。运行时正确地告诉您它是Rextester.A

另一方面,编译器不知道这一点。 变量 被声明为ISampleInterface,因此编译器可以安全地做的就是绑定到接口定义的方法。

如果您将变量转换A,那么您可以访问其成员:

A newA = a;
newA.myValue = 15;  // perfectly valid

请注意,a 引用的实际对象没有改变(这意味着它是同一个对象 - 显然您更改了它的一个成员字段)。您仍然可以通过a 引用is 作为接口或通过newA 引用类。

如果您询问编译器a 是什么类型但使用如下方法:

public Type GetCompileTimeType<T>(T inputObject)
{
    return typeof(T);
}

(感谢this answer

并调用

Console.WriteLine(GetCompileTimeType(a));

你会得到Rextester.ISampleInterface

【讨论】:

  • "运行时正确地告诉你它是一个 Rextester.A" 对此不确定。为什么?在运行时,它仍然根本不是A。无法访问它的成员,因为它们超出了ISampleInterface 范围。
  • @markzzz - 是的。这些演员表就是 Eric Lippert 所说的 representation-preserving conversions。我认为在 C++ 中它类似于reinterpret_cast&lt;&gt; - 你没有改变位/字节,你没有执行一些转换例程。它仍然是“同一件事”。 ReferenceEquals((A)a, a) 返回true
  • 铸造根本不改变底层对象。对值类型的强制转换将为您提供所请求类型的 new 对象——对引用类型 的强制转换将为您提供对同一对象的不同引用。我也来自 C++ 背景,所以理解引用类型可能很棘手。只要意识到 C# 不是 C++ 的扩展,因此如果您尝试将其转换为 C++,某些语法可能会令人困惑。
  • “引用类型的强制转换将为您提供对同一对象的不同引用” - 仅当强制转换再次是 Eric 所指的保留表示转换的转换之一时。最终调用转换运算符的强制转换(通常)不会返回相同的对象。
  • @Damien_The_Unbeliever 很公平,也有例外。我的主要观点是投射引用不会改变实际对象。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-01-03
  • 2020-07-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-13
  • 2023-04-09
相关资源
最近更新 更多