C#/.NET:`typeof(variable)`是一种可能的语言特性吗?

9

在几个不同的场合,我试图从一个离声明较远的变量中获取它的声明类型,但是发现 typeof(T) 只能用于类型名称。

我想知道是否有任何破坏性的更改可以允许使用 typeof(variable)

例如,使用以下代码:

class Animal { /* ... */ }
class Goat : Animal { /* ... */ }

/* ... */

var g = new Goat();
Animal a = g;

Console.WriteLine(typeof(Goat));
Console.WriteLine(typeof(Animal));
Console.WriteLine(g.GetType());
Console.WriteLine(a.GetType());

您会得到类似以下的内容:

山羊
动物
山羊
山羊

为什么不能这样做:
Console.WriteLine(typeof(g));
Console.WriteLine(typeof(a));

山羊
动物

我粗略地浏览了规范,没有发现任何冲突。我认为在使用 typeof 运算符时,它将清除“为什么选择这种类型?”的疑问。

我知道编译器在这里是有能力的。使用扩展方法实现实际上是微不足道的:

public static Type TypeOf<T>(this T variable)
{
    return typeof(T);
}

但那感觉很脏,滥用编译器的类型推断。


你想如何使用它?为什么不能使用“is”来完成? - Dirk Vollmar
1
@John - 你为什么想要声明类型而不是基础类型? - Justin Niessner
2
我猜typeof()在编译时被转换为实际的Type实例,因此不能与实例一起使用。typeof(T);之所以有效是因为.NET中的泛型不是C++模板;所有类型都在编译时确定。 - user1228
1
@John - 那不是争论,而是一个诚实的问题。我想知道你希望在什么情况下使用那种功能(我很好奇你何时需要那种行为)。 - Justin Niessner
1
@John,如果你想知道一个变量是否可以从一个对象赋值,为什么不直接使用“as”或“is”? IPlugin a = b as IPlugin - tnyfst
显示剩余12条评论
4个回答

4
我认为问题在于.GetType()比typeof()旧。在C#的某个时期,您需要这样做。
"0".GetType()

为了获取String类型(例如),直到typeof()出现之前才有此功能。我认为,如果这个概念是原始语言设计的一部分,那么它可能像你描述的那样工作。由于typeof()是语言的后期引入,设计者不得不做出选择:过时/弃用/删除.GetType()(在此过程中使许多使用变得过时),使typeof()与GetType()重叠(这是您所要求的),或使typeof()的用法不与GetType()重叠。我认为C#的开发人员选择不让功能重叠(以保持简单和清晰),因此typeof()在今天的限制方式下使用。

好的,但我想听听语言设计者对这个决策过程的看法。 - John Gietzen
1
@gmagana:你的回答似乎假设GetTypetypeof执行相同的操作。但事实并非如此。 - Dirk Vollmar
1
gmagana:我不知道你是在编造还是只是记错了,但 typeof 一直都是 C# 的一部分。我有一个1999年的预发布规范,其中就包含了它。虽然缺少 is 运算符,但它在 C# 1.0 发布之前就已经添加进去了。 - Gabe
2
什么功能重叠?GetType()获取对象实例的类型。typeof()获取类型的实际System.Type。它们完全不同,这就是为什么整个问题对我来说毫无意义的原因。 - Sergey
@Gabe,你是正确的,我的记忆是有选择性的。ECMA标准的第一个草案中包含了typeof()关键字。我曾经确信它没有,但你是正确的。请参见http://www.ecma-international.org/publications/files/ECMA-ST-WITHDRAWN/ECMA-334,%201st%20edition,%20December%202001.pdf。 - Gabriel Magana
显示剩余2条评论

3
假设允许这样做:
class Animal { }

string Animal;
Type t = typeof(Animal); // uh-oh!

2
这并不一定是个问题... 在您的情况下,typeof() 可以像对 Animal 符号的任何其他引用一样行事:局部变量胜出。 - Gabriel Magana
3
@gmagana,如果局部变量获胜,那么你刚刚破坏了许多现有的代码。如果类型获胜,则此语法非常危险,因为很难知道当前范围内每个类型。 - tnyfst
1
@gmagana:所以你不喜欢 public Color Color { get; set; } 吗? - jason
1
@John Gietzen:你的例子导致了编译时错误。这是你的意图吗? - jason
1
@Jason。是的,那就是重点。为什么在你指定的情况下无法引发编译时错误呢?我认为,如果这是一个问题,你可以在存在冲突的情况下引发一个错误(或者可能是警告)。再次提醒你,我说的是typeof(variable),而不是typeof(expression) - John Gietzen
显示剩余4条评论

0

您似乎在这里提出了两个不同的问题,第一个是为什么没有用于查找已声明变量类型的运算符,另一个是为什么typeof(o)不能等同于o.GetType()

对于第一个问题,原因是它没有任何作用,变量的声明类型在编译时就已经确定。添加一个运算符对其进行操作将没有任何价值。

对于另一个问题,问题在于使用typeof(instance)会导致解析问题。正如Jason上面提到的那样,请考虑:

public class Animal {}
string Animal;
Type t = typeof(Animal);

t是什么?如果你将t视为typeof(string),那么你只是在语言中添加了大量的破坏性变化。想象一下,所有当前假定t是Animal的代码都是正确的。

但是,如果你将t视为typeof(Animal),那么你的代码就非常脆弱。想象一下这种情况:当你编写代码时,没有Animal类,但一年后有人在你导入的某个命名空间中添加了一个Animal类。你的代码会崩溃,因为解析规则现在将使用Animal类型而不是局部变量。


1
不,我不希望 typeof(o) 等同于 o.GetType()。这根本不是我想要的。我希望 typeof(o) 是一种查找声明类型 o 的方法,类似于 typeof(T) 在类型名称上的工作方式。我意识到存在名称冲突,就像Jason所说的那样,但是为什么要在语言中允许名称冲突呢? - John Gietzen
1
@John,我在回答的第一部分提到了“获取声明类型”的问题;它在编译时已知,因此没有任何作用。至于第二个问题,我不知道你是没有看到这个破坏性变化还是觉得破坏性变化不重要。你的问题是这是否会是一个破坏性变化;答案是肯定的。 - tnyfst
说类型在编译时已知,因此“没有用处”有点奇怪。你是在说你想不到访问变量声明类型而不是特定类型的用例吗? - John Gietzen
@John,是的,我想不到任何需要在运行时确定变量声明类型的情况,也没有任何情况不这样做会增加脆弱性和混乱性。为什么你要这样做呢?至于破坏性更改的数量,将属性命名为它返回的类型是非常常见的;我不知道你为什么认为这很少见。 - tnyfst
@John,你在这个问题上反复无常。在你提出的语法中,typeof(AmbiguousPropertyAndTypeName)是一个编译错误还是会静默地解析为类型名?前者是一个巨大的破坏性变更,后者则非常脆弱。 - tnyfst
显示剩余3条评论

0
如果您只是想获取某个东西的类型,您可以直接调用。
Type t1 = myObject.GetType();

它应该给你想要的行为。


1
这并不是获取声明类型,而是获取所引用对象的最具体类型。声明类型在运行时基本上是不可见的,但在反射中很有用。 - John Gietzen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接