C# 为什么为空<;T>;由PropertyInfo.SetValue进行特殊处理
在实现类似于C# 为什么为空<;T>;由PropertyInfo.SetValue进行特殊处理,c#,reflection,type-conversion,C#,Reflection,Type Conversion,在实现类似于Nullable的结构时,我发现PropertyInfo.SetValue对Nullable类型的处理与其他类型不同。 对于Nullable属性,它可以设置基础类型的值 foo.GetType().GetProperty("NullableBool").SetValue(foo, true); 但对于自定义类型,它抛出 System.ArgumentException:类型为“SomeType”的对象无法转换为类型NullableCase.CopyOfNullable 1[Some
Nullable
的结构时,我发现PropertyInfo.SetValue
对Nullable
类型的处理与其他类型不同。
对于Nullable属性,它可以设置基础类型的值
foo.GetType().GetProperty("NullableBool").SetValue(foo, true);
但对于自定义类型,它抛出
System.ArgumentException:类型为“SomeType”的对象无法转换为类型NullableCase.CopyOfNullable 1[SomeType]
即使所有转换运算符都以与原始Nullable
要复制的代码:
using System;
namespace NullableCase
{
/// <summary>
/// Copy of Nullable from .Net source code
/// without unrelated methodts for brevity
/// </summary>
public struct CopyOfNullable<T> where T : struct
{
private bool hasValue;
internal T value;
public CopyOfNullable(T value)
{
this.value = value;
this.hasValue = true;
}
public bool HasValue
{
get
{
return hasValue;
}
}
public T Value
{
get
{
if (!hasValue)
{
throw new InvalidOperationException();
}
return value;
}
}
public static implicit operator CopyOfNullable<T>(T value)
{
return new CopyOfNullable<T>(value);
}
public static explicit operator T(CopyOfNullable<T> value)
{
return value.Value;
}
}
class Foo
{
public Nullable<bool> NullableBool { get; set; }
public CopyOfNullable<bool> CopyOfNullablBool { get; set; }
}
class Program
{
static void Main(string[] args)
{
Foo foo = new Foo();
foo.GetType().GetProperty("NullableBool").SetValue(foo, true);
foo.GetType().GetProperty("CopyOfNullablBool").SetValue(foo, true); //here we get ArgumentException
}
}
}
使用系统;
命名空间NullableCase
{
///
///从.Net源代码复制可为null的
///为了简洁,没有不相关的方法
///
公共结构CopyOfNullable,其中T:struct
{
私有布尔值;
内部T值;
可用的公共副本(T值)
{
这个值=值;
this.hasValue=true;
}
公共布尔值
{
得到
{
返回值;
}
}
公共价值
{
得到
{
如果(!hasValue)
{
抛出新的InvalidOperationException();
}
返回值;
}
}
公共静态隐式运算符CopyOfNullable(T值)
{
返回新的CopyOfNullable(值);
}
公共静态显式运算符T(CopyOfNullable值)
{
返回值;
}
}
福班
{
公共可空NullableBool{get;set;}
public CopyOfNullableBool{get;set;}
}
班级计划
{
静态void Main(字符串[]参数)
{
Foo-Foo=新的Foo();
GetType().GetProperty(“NullableBool”).SetValue(foo,true);
foo.GetType().GetProperty(“CopyOfNullablobool”).SetValue(foo,true);//这里我们得到ArgumentException
}
}
}
为什么PropertyInfo.SetValue
对于CopyOfNullable
类型失败,并传递给Nullable
?Nullable
在CLR类型系统中具有特殊支持,可以从T
自动转换
事实上,不可能有一个可为null的装箱实例;“可为空的值”框设置为基础值或实际为空
这是BCL中为数不多的魔法类型之一;无法复制。调用.SetValue()时,调用树如下所示:
- System.Reflection.RuntimePropertyInfo.SetValue(对象obj,对象 值,对象[]索引)
- System.Reflection.RuntimePropertyInfo.SetValue(对象obj,对象 值,BindingFlags invokeAttr,绑定器绑定器,对象[]索引, 文化信息(文化)
- System.Reflection.RuntimeMethodInfo.Invoke(对象obj,BindingFlags invokeAttr,绑定器绑定器,对象[]参数,CultureInfo区域性)
- System.Reflection.RuntimeMethodInfo.InvokeArgumentsCheck(对象obj, BindingFlags invokeAttr,Binder Binder,对象[]参数, 文化信息(文化)
- System.Reflection.MethodBase.CheckArguments(对象[]参数, Binder Binder、BindingFlags invokeAttr、CultureInfo区域性、签名符号)
- System.RuntimeType.检查值(对象值,活页夹 binder、CultureInfo区域性、BindingFlags invokeAttr)
- System.RuntimeType.TryChangeType(对象值、绑定器、, CultureInfo区域性,布尔值需要特殊广播)[仅当您使用自定义类型时才会调用此选项]
IsInstanceOfType(value)
返回true,框架将调用它,这将允许该方法基于泛型类型值确定相等性
正如我们所知,可空类型是特殊的,并且具有额外的语言支持(想想如何使用int?
而不是Nullable
)。当您的自定义类型遍历此相等检查时,它将不会被视为相等实例,而是继续向下到逻辑树,将其视为单独的类型,并调用System.RuntimeType.TryChangeType
如果我们在IsInstanceOfType
中进一步深入研究源代码,我们会发现RuntimeTypeHandle.CanCastTo
用于确定相等性,并将键入委托给VM(在这种情况下,可空类型基于版本烘焙到VM中,因为框架中的可空类型被修饰为[System.Runtime.Versioning.NonVersionable]
)
希望这告诉你的是,
Nullable
类型在框架中有特殊的支持,无法复制。由于反射利用了这种支持,你将无法复制Nullable
的一些细微差别。你是说int?
没有被C语言翻译成Nullable吗
,这是一个CLR函数来实现吗?@ClickRick我已经查看了,从我所知道的,编译器不会将int?
编译为Nullable
,它们作为单独的语法节点保留。事实上,如果你反编译一个同时使用int?
和Nullable
的应用程序,你可以看到语法保留了编译器也是如此。这让我相信?
是一个CLR构造。@MariusDornean:这完全错误。int?
是Nullable
的语法糖。Roslyn语法树始终保留原始语法,但语义模型将具有相同的类型。事实上,您可以看到实际使用的Roslyn编译器代码ly将T?
转换为Nullable
:@SLaks感谢您澄清这一点!除此之外,它还有什么特别的支持
RuntimeType runtimeType;
if (this.IsInstanceOfType(value))
{
Type type = null;
RealProxy realProxy = RemotingServices.GetRealProxy(value);
type = (realProxy == null ? value.GetType() : realProxy.GetProxiedType());
if (type == this || !RuntimeTypeHandle.IsValueType(this))
{
return value;
}
return RuntimeType.AllocateValueType(this, value, true);
}
if (!base.IsByRef)
{
if (value == null)
{
return value;
}
if (this == RuntimeType.s_typedRef)
{
return value;
}
}
// For runtime type, let the VM decide.
if (fromType != null)
{
// both this and c (or their underlying system types) are runtime types
return RuntimeTypeHandle.CanCastTo(fromType, this);
}