【发布时间】:2011-05-11 04:21:43
【问题描述】:
从互联网上的各种来源,我收集了以下功能:
public static Nullable<T> TryParseNullable<T>(this Nullable<T> t, string input) where T : struct
{
if (string.IsNullOrEmpty(input))
return default(T);
Nullable<T> result = new Nullable<T>();
try
{
IConvertible convertibleString = (IConvertible)input;
result = new Nullable<T>((T)convertibleString.ToType(typeof(T), CultureInfo.CurrentCulture));
}
catch (InvalidCastException) { }
catch (FormatException) { }
return result;
}
我把它做成了一个扩展方法,直接调用就可以了:
int? input = new int?().TryParseNullable("12345");
当我尝试在另一个泛型函数的上下文中使用反射调用它时,就会出现我的问题。 SO 充满了描述如何获取泛型方法和静态方法的 MethodInfo 的答案,但我似乎无法以正确的方式将它们放在一起。
我已经正确确定传递的泛型类型本身就是泛型类型(Nullable<>),现在我想使用反射来调用Nullable<> 上的TryParseNullable 扩展方法:
public static T GetValue<T>(string name, T defaultValue)
{
string result = getSomeStringValue(name);
if (string.IsNullOrEmpty(result)) return defaultValue;
try
{
if (typeof(T).IsGenericType && typeof(T).GetGenericTypeDefinition() == typeof(Nullable<>))
{
MethodInfo methodInfo;
//using the TryParse() of the underlying type works but isn't exactly the way i want to do it
//-------------------------------------------------------------------------------------------
NullableConverter nc = new NullableConverter(typeof(T));
Type t = nc.UnderlyingType;
methodInfo = t.GetMethod("TryParse", BindingFlags.Public | BindingFlags.Static, Type.DefaultBinder, new[] { typeof(string), t.MakeByRefType() }, null);
if (methodInfo != null)
{
var inputParameters = new object[] { result, null };
methodInfo.Invoke(null, inputParameters);
return (T) inputParameters[1];
}
//start of the problem area
//-------------------------
Type ttype = typeof(T);
//this works but is undesirable (due to reference to class containing the static method):
methodInfo = typeof(ParentExtensionsClass).GetMethod("TryParseNullable", BindingFlags.Public | BindingFlags.Static);
if (methodInfo != null)
Console.WriteLine(methodInfo);
//standard way of getting static method, doesn't work (GetMethod() returns null):
methodInfo = ttype.GetMethod("TryParseNullable", BindingFlags.Public | BindingFlags.Static);
if (methodInfo != null)
Console.WriteLine(methodInfo);
//Jon Skeet's advised method, doesn't work in this case (again GetMethod() returns null):
//(see footnote for link to this answer)
methodInfo = ttype.GetMethod("TryParseNullable");
methodInfo = methodInfo.MakeGenericMethod(ttype);
if (methodInfo != null)
Console.WriteLine(methodInfo);
//another random attempt (also doesn't work):
methodInfo = ttype.GetMethod("TryParseNullable", BindingFlags.Public | BindingFlags.Static, Type.DefaultBinder, new[] { typeof(string) }, null);
if (methodInfo != null)
Console.WriteLine(methodInfo);
}
// if we get this far, then we are not handling the type yet
throw new ArgumentException("The type " + defaultValue.GetType() + " is not yet supported by GetValue<T>.", "T");
}
catch (Exception e)
{
[snip]
}
}
谁能让我摆脱痛苦?typeof(T) 返回正确的类型信息,我想我可能在 GetMethod() 调用中使用它有点不正确,或者我没有在调用 GetMethod() 时指定正确的参数。
【问题讨论】:
-
您确定扩展方法被视为 typeof(T) 的一部分吗?我的猜测是你需要从实现扩展方法的静态类中检索方法,句号。您是否尝试过仅调用 GetMethods() 并检查返回的内容?
-
谢谢@dlev,这就是答案。扩展方法的真正基本原理之一,我忽略了它。
-
只是关于您的逻辑的注释,如果
input == null,您为什么将default(T)作为T?返回?似乎“更正确”的逻辑是返回 null。 -
还值得注意的是,您的反射逻辑也可能存在谬误。扩展方法是扩展所有
Nullable<T>,其中T是任何有效类型之一,而不是Nullable<>。Nullable<>是它自己的类型,表示没有指定类型参数的类型。它通常只对反思有用,因为它是一个半生不熟的课程,但这是手头的主题。您实际上无法实例化Nullable<>。
标签: c# generics reflection extension-methods