关于c#:为什么我们必须使用typeof,而不仅仅是使用类型?


Why do we have to use typeof, instead of just using the type?

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

1
foo.NetType = bool;

编译器生成此警告:

"Expression expected."

解决这个问题的方法是:

1
foo.NetType = typeof(bool);

我的问题是为什么我们不能使用第一种方法?编译器是否足够聪明,不知道我们要在这里完成什么?我们为什么要采用第二种方法(typeof)有什么原因吗?


好问题——就语言设计中的一个有趣问题而言。对于这个站点来说,这可能不是一个理想的问题,因为它不涉及具体的、实际的代码。好的。

设计一种没有显式typeof运算符的语言,将类型名用作表达式是完全可行的。好的。

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

1
2
3
4
5
6
7
enum Color { Red }
class Square
{
    Color Color { get; set; }
    void M()
    {
        Type t = Color.GetType();

在C今天,这明确意味着调用属性color的getter并对结果调用gettype。(请参阅规范的"颜色规则"部分了解原因。)在您建议的世界中,这可能意味着三件事:好的。

  • 为color调用getter,对结果调用gettype,将color的type对象赋给t。
  • 颜色为类型颜色生成一个类型对象。对该类型调用GetType,并将System.Type的类型对象赋给T。
  • color引用类型color,gettype引用非静态方法,这是一个错误,因为这是对非静态成员的静态调用。

您需要澄清规范,以便在表达式中的颜色意味着类型以及何时意味着生成类型对象时清晰可见。因此,所提出的特性增加了一些必须处理的模糊性,但它是完全可行的。语言设计者可以想出合理的规则。好的。

一种语言,它允许通常是编译时分析的一部分的语言元素被解释为创建一个对象的代码,该对象可以被同一程序中的代码操纵,这种语言被称为同一标志性语言。C在C 3之前是一种非常非同形的语言;表达树使得C更具同形性。C 3允许将lambda视为程序元素,这些程序元素随后用于生成执行lambda主体操作的方法,但它还支持从lambda到表示lambda主体的对象的同形转换。好的。

Lisp是最具同形性的语言之一;Lisp操作列表,任何Lisp程序本身都可以作为列表;它可以作为对象而不是代码在运行时引用和操作。所以我们再一次看到关于语言设计的老笑话的真相:每个语言设计师最终都会重新发明Lisp,很糟糕。好的。

我离题了。那么,你的问题本质上是:C应该或多或少地与类型名具有同形性吗?在类似这样的上下文中,类型名应该有一个含义——编译时指令好的。

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

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

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

?好的。

虽然设计这样的语言是可能的,但我不太喜欢。如果没有一个运算符在其中明确地调用"hey,we are using what is normally a compile time element in a homographic manufacturer",这可能会令人困惑。在C 3之前,没有其他的同形语言特性,因此只有一个特性似乎有点奇怪,那就是类型可以同时用作类型或表达式,从而生成类型对象。在某些表达式中,不清楚某个特定的简单名称是指"我在编译时使用这种类型"还是"我想创建一个对象",这确实显得很不幸。好的。

有一个显式类型的运算符可以减轻所有这些问题;当使用的类型是同形的并且对读者非常清楚时,这是明确的。好的。

要解决关于您的问题的非常具体的一点:好的。

When trying to assign a type to a property of type System.Type...

Ok.

C一般来说,没有只适用于作业的特殊规则。一个表达式的意义通常是确定的,而不是对使用该表达式的上下文有吸引力。当我们说:好的。

1
x = y;

我们一般不会说"哦,我知道y被分配给x,x是x的类型,所以我将在y的分析中使用这个信息"。相反,分析从内到外进行:我们计算出y的含义,然后决定它是否与x兼容。好的。

当然,这个规则的例外是lambda;lambda确实考虑了它们的"target"类型,因为可以使用目标类型来推断隐式类型lambda的类型。把这些规则弄对是非常复杂的。好的。

一般来说,让赋值表达式变得特殊是个坏主意。有很多方法可以将值分配给变量:好的。

1
2
3
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.

您希望所有这些赋值的规则是一致的;如果表达式表示"我是类型对象",那么它应该表示在该表达式可以出现的每个上下文中,不仅仅是赋值的右侧。好的。好啊。


对不起,我一开始误解了你的目标。不过,你还是有点困惑。

您试图将Type对象的实例分配给属性。bool不是Type类的实例,它是自己的类型。我很欣赏这个术语有点混淆,但它们是两个不同的东西。这就是它不起作用的原因。


下面是一个小家伙:

如果我有课:

1
2
3
4
public class Foo
{
    public static int Bar = 1;
}

我有一些代码,比如:

1
var foo = Foo;

你可能会说"好吧,那一定是指那种类型"

如果我有这样的东西:

1
2
3
4
5
6
7
public class Bar
{
    public void Foo ()
    {
          var hmm = Foo;
    }
}

我什么意思?foo型?方法如何?命名空间(如果存在)foo?

通过用typeof包装它,我们可以明确地表达我们想要什么。另外,它会生成特定的IL,但我假设您的问题隐含地意味着"为什么编译器不更智能?"