看标题是不是很迷惑,其实用代码描述更直观。就是现在知道泛型类定义G<T>
,也知道T对应的Type
,如何获取G<T>
对应的Type
。
现在有如下类,符合上面描述对应的类定义。
public class TestObj
{
public string ObjId { get; set; } = Guid.NewGuid().ToString();
}
public class TestObjOne : TestObj
{
public string Name { get { return "One"; } }
}
public class TestResult<T>
where T : TestObj
{
public string SomeProp { get; set; }
public T Object { get; set; }
}
也就是说我现在知道有泛型类TestResult<T>
,也知道T对应的类型是typeof(TestObjOne)
,如何得到typeof(TestResult<TestObjOne>)
?
也许有人会说,上面的typeof(TestResult<TestObjOne>)
不是已经拿到想要的类型了吗?但问题在代码里这是硬编码啊,就上面描述的场景写不出来的啊!
通过对比typeof(TestResult<>)
和typeof(TestResult<TestObjOne>)
,可以发现Type.AssemblyQualifiedName
非常的有规律,两者的差异在于方括号以内。
TestResult<>:
ConsoleDemo.TestResult`1, ConsoleDemo, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
TestResult<TestObjOne>:
ConsoleDemo.TestResult`1[[ConsoleDemo.TestObjOne, ConsoleDemo, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]], ConsoleDemo, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
那这样是不是可以通过字符串的操作,得到最终想要的Type
呢?测试下来,的确可以,所以有了下面的代码:
public static Type GetGenericTypeWithParameter(Type tParamType,Type genericType)
{
if (tParamType == null)
{
throw new ArgumentNullException(nameof(tParamType));
}
if (genericType == null)
{
throw new ArgumentNullException(nameof(genericType));
}
if (!genericType.IsGenericType)
{
throw new ArgumentException("Not generic type");
}
if (genericType.GenericTypeArguments.Length > 0)
{
throw new ArgumentException("Generic type has arguments");
}
if (tParamType.IsAbstract || tParamType.IsInterface)
{
throw new ArgumentException("Not supported");
}
var gTypeString = genericType.AssemblyQualifiedName;
var idx = gTypeString.IndexOf(',');
var fString = $"{gTypeString.Substring(0, idx)}[[{tParamType.AssemblyQualifiedName}]]{gTypeString.Substring(idx)}";
return Type.GetType(fString);
}
好吧,现在有这样一个场景,有一段字符串是typeof(TestResult<TestObjOne>)
对应Json序列化后的结果,但最终返回给外部时为了统一返回返回的是TestResult<TestObj>
,然后发现问题又来了,下面的代码编译过不了
static TestResult<TestObj> Get()
{
return new TestResult<TestObjOne>();
}
换个思路尝试,编译不报错,返回null了……
static TestResult<TestObj> Get()
{
return new TestResult<TestObjOne>() as TestResult<TestObj>;
}
有人可能说那就考虑协变逆变啊,但协变逆变是基于接口定义的,为了这个将贫血模型还要定义接口好像有点过了,而且协变逆变用起来也有点难度。
那是不是还有其它方式呢?事实肯定是的啊,我们用最笨的方法,自己new实例返回呗!
var jsonStr="";
var type = GetGenericTypeWithParameter(typeof(TestObjOne),typeof(TestResult<>));
dynamic data = JsonConvert.DeserializeObject(jsonStr, type);
if(data!=null)
{
return new TestResult<TestObj>
{
SomeProp=data.SomeProp,
Object=data.Object,
};
}
return null;
注意上面的代码最后用了dynamic
,这样就可以不用通过再反射赋值的方式来处理,当然如果泛型类属性很多的话,也可以引入Mapper类来避免手工赋值的情况。