13

当尝试将类型分配给 type 的属性时System.Type,为什么我们不能这样做?

foo.NetType = bool;

编译器会产生以下警告:

“意料之中的表情。”

解决它的方法是这样做:

foo.NetType = typeof(bool);

我的问题是为什么我们不能使用第一种方法?编译器还不够聪明,无法弄清楚我们要在这里完成什么吗?为什么我们必须采用第二种方法(typeof)有什么原因吗?

4

3 回答 3

21

好问题——只要它是语言设计中的一个有趣问题。对于本网站来说,这可能不是一个理想的问题,因为它与具体的实际代码无关。

设计一种无需显式运算符即可将类型名称用作表达式的语言是完全可行的typeof

这样做需要在语言中添加少量额外的规则和说明。例如,假设我们有:

enum Color { Red }
class Square 
{
    Color Color { get; set; }
    void M()
    {
        Type t = Color.GetType(); 

在今天的 C# 中,这明确意味着调用属性 Color 的 getter 并在 result 上调用 GetType。(请参阅规范的“颜色规则”部分以了解原因。)在您提出的世界中,这可能意味着三件事:

  • 调用 Color 的 getter,对结果调用 GetType,将 Color 的 Type 对象分配给 t。
  • Color 会产生一个 Color 类型的 Type 对象。对该类型调用 GetType,并将 System.Type 的 Type 对象分配给 t。
  • Color 指的是 Color 类型,GetType 指的是非静态方法,这是错误的,因为这是对非静态成员的静态调用。

您需要澄清规范,以便清楚表达式中的 Color 何时表示类型以及何时表示创建类型 object。因此,提议的功能增加了必须处理的少量歧义,但它是完全可行的。语言设计者可以提出合理的规则。

允许通常是编译时分析的一部分的语言元素被解释为创建可以由同一程序中的代码操作的对象的代码的语言称为同音语言。在 C# 3 之前,C# 是一种非常不和谐的语言;表达式树使 C# 更加谐音。C# 3 允许将 lambda 视为程序元素,然后用于生成执行 lambda 主体操作的方法,但它还支持从 lambda 到表示 lambda 主体的对象的同音变换。

最谐音的语言之一是 Lisp。Lisp 操作列表,任何 Lisp 程序本身都可以被认为是一个列表。它可以在运行时作为对象而不是代码来引用和操作。因此,我们再次看到了关于语言设计的老笑话的真相:每个语言设计师最终都会重新发明 Lisp,非常糟糕。

我跑题了。那么您的问题本质上是:C# 在类型名称方面应该或多或少同音吗?类型名称是否应该有一个含义——编译时指令——在上下文中

Foo x = new Foo();
object o = new List<Foo>[] {};
Foo.StaticMethod();

并且具有非常不同的含义-作为可以在其他上下文中在运行时检查的对象的构造:

object t = Foo; // Assign a Type object to t.

?

虽然设计这样的语言当然是可能的,但我不太喜欢它。如果没有一个操作员清楚地指出“嘿,我们正在以同音方式使用通常是编译时元素”,这可能会令人困惑。在 C# 3 之前,没有其他同音语言功能,因此只有一个类型可以用作类型或生成 Type 对象的表达式,这似乎有点奇怪。不幸的是,在某些表达式中,不清楚一个特定的简单名称是指“我在编译时使用这种类型”还是“我想创建一个对象”。

有一个明确的 typeof 操作符可以缓解所有这些问题;当类型被同义地使用并且对读者来说非常清楚时,它是明确的。

要解决有关您的问题的一个非常具体的问题:

尝试将类型分配给System.Type类型的属性时...

C# 通常没有只适用于赋值的特殊规则。一个表达式的含义通常是在不考虑使用该表达式的上下文的情况下确定的。当我们说:

x = y;

我们通常不会说“哦,我知道 y 被分配给 x 并且 x 属于 X 类型,所以我将在分析 y 时使用该信息”。相反,分析是从内到外:我们计算出 y 的含义,然后决定它是否与 X 兼容。

这个规则的例外当然是 lambdas。lambda确实考虑了它们的“目标”类型,因为目标类型可用于推断隐式类型 lambda 的类型。使这些规则正确是非常复杂的。

更一般地说,使赋值表达式特别是一个坏主意。有很多方法可以将值分配给变量:

M(x); // x is assigned to a formal
q = new [] { x }; // x is assigned to the first element of an array
y = new Y() { X = x }; // x is assigned to a property of Y.

您希望所有这些分配的规则保持一致;如果一个表达式的意思是“我是一个类型对象”,那么它应该意味着在该表达式可以出现的每个上下文中,而不仅仅是作为赋值的右侧。

于 2013-03-21T06:04:33.283 回答
6

对不起,我一开始误解了你的目标。但是,您仍然有些困惑。

您正在尝试将对象的实例分配Type给属性。bool不是Type类的实例,它是它自己的类型。我很欣赏这个术语有点令人困惑,但它们是两个不同的东西。这就是为什么它不起作用。

于 2013-03-20T21:59:52.043 回答
2

所以这里有一个 SWAG:

如果我有一堂课:

public class Foo
{
    public static int Bar = 1;
}

我有一些代码,例如:

var foo = Foo;

你可能会说“好吧,这绝对意味着类型”

现在,如果我有类似的东西:

public class Bar
{
    public void Foo ()
    {
          var hmm = Foo;
    }
}

我是什么意思?类型 Foo? 方法 Foo? 命名空间(如果存在的话) Foo?

通过将它包装在 typeof 中,我们可以明确我们想要什么。此外,它会生成特定的 IL,但我假设您的问题隐含的意思是“为什么编译器不更聪明?”

于 2013-03-20T22:57:37.157 回答