views:

357

answers:

7
+7  Q: 

Generic TryParse

I am trying to create a generic extension that uses 'TryParse' to check if a string is a given type:

public static bool Is<T>(this string input)
{
    T notUsed;
    return T.TryParse(input, out notUsed);
}

this won't compile as it cannot resolve symbol 'TryParse'

As I understand, 'TryParse' is not part of any interface.

Is this possible to do at all?

Update:

Using the answers below I have come up with:

public static bool Is<T>(this string input)
{
    try
    {
        TypeDescriptor.GetConverter(typeof(T)).ConvertFromString(input);
    }
    catch
    {
        return false;
    }

    return true;
}

It works quite well but I think using exceptions in that way doesn't feel right to me.

Update2:

Modified to pass type rather than use generics:

public static bool Is(this string input, Type targetType)
{
    try
    {
        TypeDescriptor.GetConverter(targetType).ConvertFromString(input);
        return true;
    }
    catch
    {
        return false;
    }
}
+4  A: 

You can't do it on general types.

What you could do is to create an interface ITryParsable and use it for custom types that implement this interface.

I guess though that you intend to use this with basic types like int and DateTime. You can't change these types to implement new interfaces.

Mark Byers
I wonder if that would work by using the dynamic keyword in .net 4?
Pierre-Alain Vigeant
@Pierre: This won't work by default in C# with the `dynamic` keyword, because it won't work on static typing. You can create your own dynamic object that can handle this, but it is not default.
Steven
A: 

This is a question of 'generic constraints'. Because you don't have a specific interface then you are stuck unless you follow the suggestions of the previous answer.

For documentation on this, check the following link:

http://msdn.microsoft.com/en-us/library/ms379564(VS.80).aspx

It shows you how to use these constraints and should give you some more clues.

Simon
+8  A: 

You should use the TypeDescriptor class:

public static T Convert<T>(this string input)
{
    var converter = TypeDescriptor.GetConverter(typeof(T));
    if(converter != null)
    {
        return converter.ConvertFromString(input);
    }
    return default(T);
}

of course this will throw an exception if the conversion fails so you will want to try/catch it.

luke
A: 

As you said, TryParse is not part of an interface. It is also not a member of any given base class since it's actually static and static functions can't be virtual. So, the compiler has no way of assuring that T actually has a member called TryParse, so this doesn't work.

As @Mark said, you could create your own interface and use custom types, but you're out of luck for the built-in types.

Donnie
+1  A: 

When I wanted to do almost this exact thing, I had to implement it the hard way, given reflection. Given T, reflect on typeof(T) and look for a TryParse or Parse method, invoking it if you've found it.

JSBangs
This is what I was going to suggest.
SnOrfus
+2  A: 

How about something like this?

http://madskristensen.net/post/Universal-data-type-checker.aspx

/// <summary> 
/// Checks the specified value to see if it can be 
/// converted into the specified type. 
/// <remarks> 
/// The method supports all the primitive types of the CLR 
/// such as int, boolean, double, guid etc. as well as other 
/// simple types like Color and Unit and custom enum types. 
/// </remarks> 
/// </summary> 
/// <param name="value">The value to check.</param> 
/// <param name="type">The type that the value will be checked against.</param> 
/// <returns>True if the value can convert to the given type, otherwise false. </returns> 
public static bool CanConvert(string value, Type type) 
{ 
    if (string.IsNullOrEmpty(value) || type == null) return false;
    System.ComponentModel.TypeConverter conv = System.ComponentModel.TypeDescriptor.GetConverter(type);
    if (conv.CanConvertFrom(typeof(string)))
    { 
        try 
        {
            conv.ConvertFrom(value); 
            return true;
        } 
        catch 
        {
        } 
     } 
     return false;
  }

This can be converted to a generic method pretty easily.

 public static T Is<T>(this string input)
 {
    if (string.IsNullOrEmpty(value)) return false;
    var conv = System.ComponentModel.TypeDescriptor.GetConverter(typeof(T));

    if (conv.CanConvertFrom(typeof(string)))
    { 
        try 
        {
            conv.ConvertFrom(value); 
            return true;
        } 
        catch 
        {
        } 
     } 
     return false;
}
Bob
Does it matter whether you return true from the try block or return false from the catch block? I suppose not, but I still think using exceptions in this way feels wrong to me...
Piers Myers
It doesn't matter whether you return from the catch block, this is the same. btw. Usually it is bad to have a generic catch clause: `catch { }`. However, in this case there is no alternative, because the .NET `BaseNumberConverter` throws the `Exception` base class in case of a conversion error. This is very unfortunate. In fact there are still quite a few places were the this base type is thrown. Hopefully Microsoft will fix these in a future version of the framework.
Steven
Thanks Steven, Couldn't have said it better.
Bob
+2  A: 

If you are set on using TryParse, you can use reflection and do it like this:

public static bool Is<T>(this string input)
{
    var type = typeof (T);
    var temp = default(T);
    var method = type.GetMethod(
        "TryParse",
        new[]
            {
                typeof (string),
                Type.GetType(string.Format("{0}&", type.FullName))
            });
    return (bool) method.Invoke(null, new object[] {input, temp});
}
Joseph Sturtevant
This is very cool, and it gets rid of the exceptions that I didn't like anyway. Still a bit convoluted though.
Piers Myers