为什么我不能在编译时知道所有名称时使用nameof?

问题描述 投票:2回答:4
public void Test<T>()
{
    Console.WriteLine(nameof(T));
}

Test<int>();

这段代码字面上打印T而不是int,这根本没用。我想获得一个实际泛型类型参数的名称,而不使用反射(typeof,然后对Type变量进行操作等)

我读到泛型的要点是在编译时准备好在定义中使用不同类型的代码的变体。而nameof也是一个编译时运算符。在这种情况下,它应该足以知道这里的T是一个int。必须有一些方法来做到这一点,而不是必须从用户端(例如Test<int>(nameof(int))

如果有人对这个用例很好奇,除了调试之外我还想把这个项目的类名作为键添加到字典中。这本词典中只有一种形状。

public AddShape<T>(T shape) where T : Shape
{
    dict.Add(nameof(T), shape.SerializableShape);
}
c# .net generics
4个回答
5
投票

nameof构造用于在编译时确定类型,变量,字段等的名称。当您需要typeat运行时,它对您没有帮助。你能做的就是使用对象的类型信息:

public AddShape<T>(T shape) where T : Shape
{
    dict.Add(shape.GetType().FullName, shape.SerializableShape);
}

2
投票

因为nameof在编译时使用类型信息,并且将使用该名称作为字符串,但是对于泛型类型,T编译器无法在编译时计算出来,因为它将作为参数传递,因为它可能是Shape或它的任何子类型,这就是它在运行时初始化的原因,这就是为什么它不能像上面那样使用的原因。

我也发现这相关也有帮助:

https://stackoverflow.com/a/29878933/1875256

希望能帮助到你


1
投票

documentationnameof的执行时间不是很清楚,但你可以找到以下句子:

不幸的是,typeof不是像nameof那样的常量表达式

我们还有the specification in the C# language repository,其中规定:

nameof_expression是string类型的常量表达式,在运行时不起作用。

这就是为什么nameof(T)返回"T"而不是你的情况下的实际类型名称的原因。因此,您需要另一种方法来在运行时获取类型名称。这是typeof运营商。来自the specification

typeof运算符可用于类型参数。结果是绑定到type参数的运行时类型的System.Type对象。

class Shape { }
class Circle : Shape { }

class Program
{
    // Output of the following code is Circle
    static void Main(string[] args) => WriteType<Circle>();

    static void WriteType<T>() where T : Shape => Console.WriteLine(typeof(T).Name);
}

如您所见,结果完全取决于WriteType<T>方法的绑定类型。这意味着如果绑定类型是Shape,那么将打印"Shape"。因此,当你迭代Shape的集合时,T的绑定类型将是Shape而不是Circle。这是因为T被推断为Shape

static void Main(string[] args)
{
    var shapes = new Shape[] { new Circle() };
    foreach (var shape in shapes)
        WriteBoundType(shape);
    // Output is "Shape"
}

static void WriteBoundType<T>(T shape)
    where T : Shape => Console.WriteLine(typeof(T).Name);

如果要获取当前形状的运行时类型,则应使用the GetType method。来自the typeof operator documentation

要获取表达式的运行时类型,可以使用.NET Framework方法GetType,如以下示例所示

这是你的情况。你既不需要typeof也不需要nameof。您甚至不需要泛型,因为您需要当前实例的运行时类型。

public AddShape(Shape shape)
{
    dict.Add(shape.GetType().Name, shape.SerializableShape);
    // You also can use the result of GetType directly or its FullName.
    // It depends on your demands which we don't know well.
}

你失败了,因为你使用的是静态类型的C ++,但是有区别。正如您所知,在C ++模板中,参数在编译时被替换,但在.NET中,它们在运行时被替换。我建议你阅读Differences Between C++ Templates and C# Generics


0
投票

使用typeof(T)作为你的密钥,nameof,如果它起作用,实际上是一个可怕的选择,因为它返回的名称是不合格的,所以没有什么能确保密钥是唯一的。

© www.soinside.com 2019 - 2024. All rights reserved.