我编写了带隐式强制转换操作符的自定义类型
public class TcBool : TcDataTypeBase
{
public TcBool() : base(1, false) { } //For somewhat reason without this callin new TcBool() fails
public TcBool(bool value = false) : base(1, value) { }
public static implicit operator bool(TcBool var) => (bool)var.Value;
public static implicit operator TcBool(bool value) => new TcBool(value);
}
public abstract class TcDataTypeBase
{
public readonly byte Size;
public readonly object Value;
public string Name { get; set; }
public int IndexGroup { get; set; }
public int IndexOffset { get; set; }
internal TcDataTypeBase(byte size, object value)
{
Size = size;
Value = value;
}
internal TcDataTypeBase(string name, byte size, object value) : this(size, value)
{
Name = name;
}
}
然后,当我尝试使用PropertyInfo.SetValue()将其写入对象的布尔属性时,它会抛出一个异常,说它不能将TcBool转换为System.Boolean。
是否存在阻止反射机制使用隐式转换或者我遗漏了什么的东西?
答案 0 :(得分:5)
编译器不知道它必须抛出任何东西,因为SetValue接受object
,它与TcBool
类型兼容,没有任何强制转换(事实上,你不能定义隐式强制转换运算符到祖先类型)。要强制演员,你可以这样做:
property.SetValue(instance, (bool)TcBool);
这将触发隐式转换运算符并创建一个布尔值,然后将其装入对象并传递给SetValue。
答案 1 :(得分:1)
编译器必须使用转换运算符。或者更确切地说,编译器指出可以应用转换并插入适当的调用。在运行时,这不会发生,并且您在这里留下了不兼容的类型,因为PropertyInfo.SetValue
只需要object
,所以从编译器的角度来看,不需要转换。 / p>
答案 2 :(得分:1)
免责声明:我意识到这可能有点过头,如果您在编译时不知道类型,则无法使用。
重要的是要知道隐式转换运算符被编译为名为"op_Implicit"
的方法,因此不会被自动调用。
所以我创建了这个(相当长的)hacky helper方法,它将类型为TObject
的对象转换为TTo
类型的对象,同时考虑了隐式转换运算符:
public static object Convert<TObject, TTo>(TObject obj)
{
IEnumerable<MethodInfo> implicitConversionOperators = obj.GetType()
.GetMethods()
.Where(mi => mi.Name == "op_Implicit");
MethodInfo fittingImplicitConversionOperator = null;
foreach (MethodInfo methodInfo in implicitConversionOperators)
{
if (methodInfo.GetParameters().Any(parameter => parameter.ParameterType == typeof(TObject)))
{
fittingImplicitConversionOperator = methodInfo;
}
}
if (fittingImplicitConversionOperator != null)
{
return fittingImplicitConversionOperator.Invoke(null, new object[] {obj});
}
return (TTo) System.Convert.ChangeType(obj, typeof(TTo));
}
当然,它远非完美,但它可以像这样使用
propertyInfo.SetValue(this, Helper.Convert<TcBool, bool>(new TcBool(true)));
设置属性。当然,如果您在编译时不知道类型/不想要那么详细,您可以尝试dynamic
等,因为它在其他答案中显示。