Search code examples
c#genericsextension-methodsconstraintsfluent-interface

Using generic constraints with value types


I am experimenting with fluent extension methods.

I have the following simple extension method to perform a safe cast.

     public static T As<T>(this Object source)
         where T : class
     {
         return source as T;
     }

This worked well, but when I tried to make it intuitive to use valuetypes with an overload

     public static T As<T>(this ValueType source)
         where T : struct
     {
         return (T)source;
     }

I ran into problems. The method resolution logic always chooses the first method above, and gives a syntax error (accurately) that the struct is not a class.

Is there a way to handle the above, or should I go the route of removing the constraint while testing for and handling all types in the same method?

==== Edit: to answer questions ====

I am compiling this against the 3.5 framework. I'm not really trying to accomplish anything in particular; this is just an experiment with the above. My interest was piqued and I threw together some code.

I'm not particularly concerned with it remaining a 'safe' cast. That is how it started, and can be kept safe with default() -- but that's not really the focus of the question and code to ensure 'safeness' would just obscure.

As to the expressiveness, no value.As<int>() is not any more expressive than (int)value; but why should the user of the method have to 'just know' it only works with reference types? My trying to work it in was more about the expected behavior of the method than expressive writing.

The code snippet value.As<DateTime>(), gives the error "The type 'System.DateTime' must be a reference type in order to use it as parameter 'T' in the generic type or method ....As(object)". From the error message I see it is resolving to use the top method above as it is the one requiring the reference type.


Solution

  • In .NET 4, the second overload is chosen based on your code sample. (Also just tested against .NET 3.5, same result.)

    int myInt = 1;
    long myLong = myInt.As<long>(); // chooses ValueType version
    

    However, this only gets us to the scene of the next crash. The (T)source; results in an invalid cast exception. You could get around that by writing the method as

    public static T As<T>(this ValueType source)
        where T : struct
    {
        return (T)Convert.ChangeType(source, typeof(T));
    }
    

    However, I wonder what you're actually looking to achieve, as I do not see the immediate benefit. (And for that matter, this isn't safe like the source as T object version.) For example, how is

    long myLong = myInt.As<long>();
    

    Any more expressive or easier to use than

    long myLong = (long)myInt;