- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我有几个月前编写的这个框架,它生成一个类来调用此性能服务。框架的消费者使用方法创建一个接口(interface),使用属性进行注释,并调用一个工厂方法来创建他们可以用来调用此性能服务的接口(interface)的实现。该服务只支持字符串和长两种数据。我将反射发射与可收集程序集结合使用来生成实现该接口(interface)的类。
一切都运行良好,但今天有人告诉我,当他们试图传入一个将被转换为字符串的枚举时,他们得到了一个 AV。在代码中,检查类型是否为值类型,如果是,则推送地址(ldarga 或 ldflda,具体取决于消费者创建的接口(interface)),然后调用 ToString。所以我创建了一个小调试应用程序,我看到 C# 编译器将封装一个枚举,然后在封装的枚举上调用 ToString。
所以我有点困惑。我处理值类型的方式不正确吗? C# 编译器为枚举上的 toString 生成的 IL 是正确的方法吗?还有其他像这样的特殊情况吗
更新答案:所以看起来我需要查看值类型是否实现了 tostring 以及它是否不装箱。对于值类型,我想这适用于对象方法、tostring、gethashcode、equals。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection.Emit;
using System.Reflection;
namespace ConsoleApplication15
{
public struct H
{
}
class Program
{
static void Main(string[] args)
{
//Test<AttributeTargets>(AttributeTargets.ReturnValue); //-- fails
//Test<int>(10); //-- works
// TestBox<AttributeTargets>(AttributeTargets.ReturnValue); //-- works
//Test<H>(new H()); // fails
TestCorrect<H>(new H()); // works
TestCorrect<int>(10); // works
Console.ReadLine();
}
private static void TestCorrect<T>(T t)
where T : struct
{
MethodInfo method = typeof(T).GetMethod(
"ToString",
BindingFlags.Public | BindingFlags.Instance,
null,
Type.EmptyTypes,
null);
var m = new DynamicMethod("x", typeof(string), new[] { typeof(T) });
var i = m.GetILGenerator();
if (method.DeclaringType == typeof(T))
{
i.Emit(OpCodes.Ldarga, 0);
i.Emit(OpCodes.Call, method);
}
else
{
i.Emit(OpCodes.Ldarg_0);
i.Emit(OpCodes.Box, typeof(T));
i.Emit(OpCodes.Callvirt, method);
}
i.Emit(OpCodes.Ret);
string result = (m.CreateDelegate(typeof(Func<T, string>)) as Func<T, string>)(t);
Console.WriteLine(result);
}
private static void Test<T>(T t)
where T : struct
{
MethodInfo method = typeof(T).GetMethod(
"ToString",
BindingFlags.Public | BindingFlags.Instance,
null,
Type.EmptyTypes,
null);
var m = new DynamicMethod("x", typeof(string), new[] { typeof(T) });
var i = m.GetILGenerator();
i.Emit(OpCodes.Ldarga, 0);
i.Emit(OpCodes.Call, method);
i.Emit(OpCodes.Ret);
string result = (m.CreateDelegate(typeof(Func<T, string>)) as Func<T, string>)(t);
Console.WriteLine(result);
}
private static void TestBox<T>(T t)
where T : struct
{
// this is how the C# compiler call to string on enum.
MethodInfo method = typeof(T).GetMethod(
"ToString",
BindingFlags.Public | BindingFlags.Instance,
null,
Type.EmptyTypes,
null);
var m = new DynamicMethod("x", typeof(string), new[] { typeof(T) });
var i = m.GetILGenerator();
i.Emit(OpCodes.Ldarg_0);
i.Emit(OpCodes.Box, typeof(T));
i.Emit(OpCodes.Callvirt, method);
i.Emit(OpCodes.Ret);
string result = (m.CreateDelegate(typeof(Func<T, string>)) as Func<T, string>)(t);
Console.WriteLine(result);
}
}
}
最佳答案
枚举类型不会覆盖其ToString()
方法,因此对于任何枚举类型e
,e.ToString()
解析到 Enum.ToString
。此方法是在引用类型上定义的(Enum
是引用类型),因此要调用此方法,隐式 this
参数需要是一个装箱值。
大多数其他值类型,例如 int
,确实直接在值类型本身上提供了一个重写的 ToString
方法。
来自规范:
I.12.1.6.2.4 Calling methods
Static methods on value types are handled no differently from static methods on an ordinary class: use a
call
instruction with a metadata token specifying the value type as the class of the method. Non-static methods (i.e., instance and virtual methods) are supported on value types, but they are given special treatment. A non-static method on a reference type (rather than a value type) expects a this pointer that is an instance of that class. This makes sense for reference types, since they have identity and the this pointer represents that identity. Value types, however, have identity only when boxed. To address this issue, the this pointer on a non-static method of a value type is a byref parameter of the value type rather than an ordinary by-value parameter.A non-static method on a value type can be called in the following ways:
For unboxed instances of a value type, the exact type is known statically. The
call
instruction can be used to invoke the function, passing as the first parameter (the this pointer) the address of the instance. The metadata token used with thecall
instruction shall specify the value type itself as the class of the method.Given a boxed instance of a value type, there are three cases to consider:
Instance or virtual methods introduced on the value type itself: unbox the instance and call the method directly using the value type as the class of the method.
Virtual methods inherited from a base class: use the
callvirt
instruction and specify the method on theSystem.Object
,System.ValueType
orSystem.Enum
class as appropriate.Virtual methods on interfaces implemented by the value type: use the
callvirt
instruction and specify the method on the interface type.
关于c# - 为什么 enum.ToString box/callvirt,而不是推送地址和调用?还有其他特殊情况吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22711443/
我不明白为什么这甚至可以编译。我尝试过不同的格式,它们似乎都有效。 为什么有一个 enum of enum of enum of.. 是合法的? interface I { enum E im
我有大型数据库(数百万行),我正在尝试为 2 个字段的数据类型做出最佳选择。我制作的大部分内容都是 varchar 或 INT。但是,我想知道 Enum 是否是最好的方法的 2 个字段。 字段 1第一
我是 C++ 新手,我想知道是否有人可以帮助我理解原因 enum difficulty { NOVICE, EASY, NORMAL, HARD, UNBEATABLE }; difficulty m
从 native 代码强制转换(在 C++/CLI 中)的正确方法是什么 enum到托管代码enum其中包含相同的 enum值(value)观?与使用 C# 的强制转换方式有什么区别,例如 (int)
我有以下枚举 [Flags] public enum WeekDays { Monday = 1, Tuesday = 2, Wednesday = 4, Thursd
我刚刚学习 Java 中的枚举。当我运行下面的代码时,我收到一个错误,我也在下面重现该错误。基本上,我的问题是:当我在枚举中定义一个方法,并且在该方法中我想检查枚举的值以便我可以根据该值执行某些操作时
我想要一些语法糖来打开 Enum .当然,一个if else块按预期工作: @enum Fruit apple=1 orange=2 kiwi=3 function talk1(fruit::Frui
我试图在 Enum.each 的函数内为变量设置一个值,但在循环结束时,变量为空,我不知道为什么会出现这种行为。 代码: base = "master" candidates = ["stream",
This question already has answers here: Is there a way to use existing structs as enum variants? (2个
我想让 Java 中的枚举器具有其他枚举作为属性。 public enum Direction { Up(Down), Down(Up), Left(Right), R
我有一个包含两种不同可能“类型”的枚举和一个可以返回其中任何一种的函数,封装在 Option 中: enum Possibilities { First(i32), Second(St
我刚刚注意到 pyhton 中 Enum+defaultdict 的一个非常奇怪的行为。我定义了一个这样的枚举,它收集了一些默认词典: from enum import Enum from colle
我想在运行时从配置文件生成一些类型。为简单起见,假设我已经将数据加载为 Python 字典: color_values = dict(RED = 1, YELLOW = 2, GREEN = 3) 我
我想创建一个方法,在传入参数的任何枚举类上实现 valueOf(并包装一些专门的错误/缺失名称代码)。所以基本上,我有几个枚举,例如: enum Enum1{ A, B, C } enum Enum2
我有一个user数据库表: CREATE TABLE IF NOT EXISTS `user` ( `user_id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
这是我的 JPA @Repository,在这里我们可以得到 list与 findByFullNameContaining(String query) - 通过在查询中提供 fullName 的子字符
(也在这里打开了一个问题:https://github.com/square/moshi/issues/768 但被要求打开一个stackoverflow问题)我正在编写一个通用适配器来转换带有枚举值
请帮助找到以下情况的最佳方法: 有一个表,大约有 20 列。 每列都有自己的短名称、全名称和类型(数字或字符串)。 每个列类型都可以有自己的运算符 - 例如,字符串 - 包含、等于;数字 - 更多、更
如果我在 python 中按功能创建了 enum.Enum(本例中为 3.7),如何从中检索类的名称? import enum def print_the_enum_class_name(some_e
我正在编写一个与第 3 方程序交互的程序。这个第 3 方程序允许用户制作可以运行在第 3 方程序中进行的步骤记录的按钮。 但! 这些按钮还可以运行用户定义的批处理文件。因此,我使用此功能通过创建文件并
我是一名优秀的程序员,十分优秀!