![](/img/trans.png)
[英]Fulfilling a new() constraint on a generic method when using System.Object
[英]Why does my generic type method think T is system.object when I'm passing in a HashSet
所以我试图弄清楚为什么我的通用方法会丢失类型信息。
当我传递反射数据时(在本例中为T = HashSet),代码认为T = system.object。 当我传递非反射数据时,我的T正确键入为HashSet类型。
我的示例中唯一的区别是,在我的第一个调用中,我直接传递了一个HashSet,而在第二个调用中,我传递了具有HashSet属性的对象,并使用反射来获取HashSet并将其传递。
两个HashSet都由完全相同的代码执行,但是T每次都不同。
我设法解决了一个“变通方法”,在该方法中,我传入了第二个参数(正确的类型),从那里我可以得到想要的结果。
但是,这似乎很棘手,我想知道为什么我必须执行此替代方法才能获得所需的结果。
谁能解释我为什么要观察自己的观察结果,以及如何每次都能使我的T型正确,而又不将其传递两次?
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
public class Program
{
public static void Main()
{
var simple = new HashSet<string>();
simple.Add("1");
simple.Add(" 1 ");
Console.WriteLine("Simple Before => " + JsonConvert.SerializeObject(simple));
simple = simple.DeepTrimAll();
Console.WriteLine("Simple After => " + JsonConvert.SerializeObject(simple));
Console.WriteLine();
var complex = new Complex();
complex.HS = new HashSet<string>();
complex.HS.Add("1");
complex.HS.Add(" 1 ");
Console.WriteLine("Complex Before => " + JsonConvert.SerializeObject(complex));
complex = complex.DeepTrimAll();
Console.WriteLine("Complex After => " + JsonConvert.SerializeObject(complex));
}
}
public class Complex
{
public HashSet<string> HS
{
get;
set;
}
}
public static class Ext
{
public static T DeepTrimAll<T>(this T context, Type t)where T : class
{
if (context is IEnumerable<string>)
{
var type = typeof (T);
Console.WriteLine("T = " + type.ToString() + " , t = " + t.ToString());
var list = new List<string>(context as IEnumerable<string>);
for (int i = 0; i < list.Count(); i++)
{
list[i] = list[i].Trim();
}
var res = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(list), t);
return (T)res;
}
var properties = typeof (T).GetProperties().Where(x => x.CanRead && x.CanWrite).ToList();
foreach (var propertyInfo in properties)
{
var propType = propertyInfo.PropertyType;
var value = propertyInfo.GetValue(context, null);
if (propType == typeof (string))
{
if (!string.IsNullOrWhiteSpace(value.ToString()))
{
propertyInfo.SetValue(context, value.ToString().Trim());
}
}
else if (!propType.IsEnum && !propType.IsPrimitive)
{
var newValue = value.DeepTrimAll(propType);
propertyInfo.SetValue(context, newValue);
}
}
return context;
}
public static T DeepTrimAll<T>(this T context)where T : class
{
return context.DeepTrimAll(typeof (T));
}
}
本示例产生以下结果:
Simple Before => ["1"," 1 "]
T = System.Collections.Generic.HashSet`1[System.String] , t = System.Collections.Generic.HashSet`1[System.String]
Simple After => ["1"]
Complex Before => {"HS":["1"," 1 "]}
T = System.Object , t = System.Collections.Generic.HashSet`1[System.String]
Complex After => {"HS":["1"]}
类型参数是在编译时而不是在执行时推断出来的(通常-稍后再介绍)。
当您调用complex.DeepTrimAll()
,它等效于:
Ext.DeepTrimAll<Complex>(complex);
然后调用DeepTrimAll<Complex>(complex, typeof(Complex))
。
现在, Complex
不实现IEnumerable<string>
,因此代码进入了if
语句的else
分支,该DeepTrimAll
递归调用DeepTrimAll
,如下所示:
var propType = propertyInfo.PropertyType;
var value = propertyInfo.GetValue(context, null);
...
var newValue = value.DeepTrimAll(propType);
现在这里value
的编译时类型是object
,因此类型推断在编译时开始 ,并将其转换为:
var newValue = DeepTrimAll<object>(value, propType);
这就是为什么要获得输出的原因。
现在,您可以在此处使用动态类型输入:
var propType = propertyInfo.PropertyType;
dynamic value = propertyInfo.GetValue(context, null);
...
var newValue = DeepTrimAll(value, propType);
不再将DeepTrimAll
称为扩展方法,因为动态类型无法处理扩展方法。 但是现在类型推断是动态执行的,因此它将调用DeepTrimAll<HashSet<string>>(value, propType)
。
我不建议这样做-我可能DeepTrimAll(T context, Type t)
您的DeepTrimAll(T context, Type t)
更改为DeepTrimAll(object context, Type t)
并始终使用t
代替typeof(T)
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.