从互联网上的各种来源我收集了以下功能:
- 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",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().
解决方法
问题是扩展方法不会修改它们正在“扩展”的类型.幕后实际发生的情况是,编译器将所有似乎在对象上进行的调用透明地转换为调用静态方法.
即.
- int? input = new int?().TryParseNullable("12345");
- // becomes...
- int? input = YourClass.TryParseNullable(new int?(),"12345");
从那里,显而易见,为什么它不会通过反思出现.这也解释了为什么你必须为命名空间使用一个using指令,其中定义了用于编译器可见的扩展方法的YourClass.至于你如何实际获得这些信息,我不知道有没有办法,运行所有声明的类型(可能是有趣的类的过滤列表,如果你知道那种编译时的信息)看对于在它们上定义的ExtensionMethodAttribute([ExtensionMethod])的静态方法,然后尝试解析参数列表的MethodInfo,以解决它们是否在Nullable上工作.