【发布时间】:2020-04-02 11:35:52
【问题描述】:
泛型函数应采用装箱 (object) 值并将其转换为指定类型(在合理范围内)。
以下内容有效,但看起来很糟糕(代码和生成的 IL)并最终无用地装箱拆箱(因为编译器不同意将原语隐式转换为泛型类型):
static T convert<T>(object val) {
var t = typeof(T);
if (t == typeof(int)) {
if (val is int) return (T)val;
if (val is double) return (T)(object)(int)(double)val;
if (val is float) return (T)(object)(int)(float)val;
if (val is bool) return (T)(object)((bool)val ? 1 : 0);
} else if (t == typeof(double)) {
if (val is double) return (T)val;
if (val is float) return (T)(object)(double)(float)val;
if (val is int) return (T)(object)(double)(int)val;
if (val is bool) return (T)(object)((bool)val ? 1.0 : 0.0);
} else if (t == typeof(float)) {
if (val is float) return (T)val;
if (val is double) return (T)(object)(float)(double)val;
if (val is int) return (T)(object)(float)(int)val;
if (val is bool) return (T)(object)((bool)val ? 1f : 0f);
} else if (t == typeof(bool)) {
if (val is bool) return (T)val;
if (val is double) return (T)(object)((double)val != 0.0);
if (val is float) return (T)(object)((float)val != 0f);
if (val is int) return (T)(object)((int)val != 0);
} else return (T)val;
throw new Exception($"Can't convert ${val} to ${typeof(T)}");
}
上下文:外部库通过这样的类实现通用函数/闭包包装器
class Function {
public Function(int argCout) { ... }
public object invoke_0() { ... }
public object invoke_1(object v0) { ... }
public object invoke_2(object v0, object v1) { ... }
// and so on...
}
你继承的,像这样:
class MyFunc : Function {
public MyFunc() : base(1) {}
public override object invoke_1(object v0) {
// (... cast v0 to appropriate type and call the destination function)
}
}
请放心,我很快就会意识到这一点:
- 虽然相当高效,但很难组织多个小类,因为它们与在代码中的使用相距甚远(不能声明类中间表达式)。
- C# 对拆箱时的类型不匹配非常宽容(例如,尝试将浮点数拆箱为双精度会引发异常)。
所以我决定做一些像这样的通用包装器:
class WrapAction<T> : Function {
Action<T> action;
public MyFunc(Action<T> act) : base(1) {
action = act;
}
public override object invoke_1(object v0) {
action.Invoke(WrapHelpers.convert<T>(v0));
return null;
}
}
(其中WrapHelpers.convert是问题开头演示的函数)。
这样就可以了
Function fun = new WrapAction<string>((str) => Console.WriteLine(str));
改为。
其他说明:
- 将
object切换为dynamic可以隐藏一些拆箱操作,但在其他方面无济于事。 - 为不同的目标类型创建不同的
convert版本/重载可以消除一些以增加行数为代价的检查(因为您的委托现在必须接受objects形式的参数,然后才能通过一个或其他函数将它们拆箱)。
【问题讨论】:
-
你试过用
Convert.ChangeType吗? -
附带说明,如果
val始终是盒装值类型,请将T约束为值类型并删除object大小写,这是荒谬的;如果您已经有了object,为什么还要打电话给convert<object>? -
object-case 的唯一目的是允许在后面显示的通用包装器中使用非特定的参数类型,尽管提到让我意识到我可以很好地在最后的 else-块(好像转换失败,和下面的
throw一样)。幸运的是,因为我现在知道 Convert.ChangeType 存在,所以这段代码将继续使用。
标签: c# type-conversion