From various sources on teh interwebs I've gleaned this following function:
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;
}
I've made it into an extension method, and it works just fine if I call it directly:
int? input = new int?().TryParseNullable("12345");
My problem occurs when I try to call it using reflection from within the context of another generic function. SO is full of answers describing how to get the MethodInfo of generic methods and static methods, but I can't seem to put these together in the right way.
I've correctly determined that the passed generic type is itself a generic type (Nullable<>
), now I want to use reflection to call the TryParseNullable
extension method on the Nullable<>
:
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]
}
}
Can someone put me out of my misery?
The typeof(T)
returns the correct type info, I figure that maybe I'm using it a little incorrectly with the GetMethod()
call, or I haven't specified the right parameters with the call to GetMethod()
.
1. Link to referenced Jon Skeet answer
You need to use reflection to get the method to start with, then "construct" it by supplying type arguments with MakeGenericMethod: MethodInfo method = typeof(Sample). GetMethod(nameof(Sample. GenericMethod)); MethodInfo generic = method.
An extension method must be defined in a top-level static class. An extension method with the same name and signature as an instance method will not be called. Extension methods cannot be used to override existing methods. The concept of extension methods cannot be applied to fields, properties or events.
You can use extension methods to extend a class or interface, but not to override them. An extension method with the same name and signature as an interface or class method will never be called. At compile time, extension methods always have lower priority than instance methods defined in the type itself.
There's no way to add them currently because the feature doesn't exist in C#.
The problem is that extension methods don't modify the type they are 'extending'. What actually happens behind the scenes is that the compiler transparently translates all the calls that seem to be made on the object in question to calls to your static method.
ie.
int? input = new int?().TryParseNullable("12345");
// becomes...
int? input = YourClass.TryParseNullable(new int?(), "12345");
From there it becomes obvious why it's not showing up via reflection. This also explains why you have to have a using
directive for the namespace where YourClass
is defined for the extension methods to be visible to the compiler. As to how you can actually get at that information, I'm not sure there is a way, short of running over all the declared types (perhaps a filtered list of interesting classes, if you know that sort of information at compile time) looking for static methods with the ExtensionMethodAttribute
([ExtensionMethod]
) defined on them, then trying to parse the MethodInfo
for the parameter list to work out if they work on Nullable<>
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With