Willem Van Onsem Willem Van Onsem - 3 months ago 18
C# Question

Conditional generic type constructor in C#?

Say you have a generic class

Foo
:

public class Foo<T> {

public T Data {
get;
protected set;
}

}


Is it possible to define a constructor that is only applicable if
T
inherits (or is) a specific type.

For instance say
T
is an
int
:

public Foo () {
this.Data = 42;
}


The type constraint should be checked at compile-time. This might be useful for optimization. Say for instance you have an
IEnumerable<T>
and you wish to make a "cache" (since LINQ queries can be quite expensive). Now if the
IEnumerable<T>
is already an
IList<T>
, it is useful not to copy data. On the other hand if it is really a LINQ query, another constructor can store the data in an array.




As a workaround, one can of course inherit
Foo
(e.g.
IntFoo
) and define a constructor there:

public class IntFoo : Foo<int> {

public IntFoo () {
this.Data = 42;
}

}


A problem with this approach is however that
private
data is not accessible (or one has to make it
protected
). Are there some other disadvantages, or does one is supposed to model type-specific constructors this way?

Answer

There's a trick that you could apply here. It's fexible for many scenarios.

internal static class FooHelper
{
    private static class DefaultData<T>
    {
        public static T Value = default(T);
    }

    static FooHelper()
    {
        DefaultData<int>.Value = 42;
        DefaultData<string>.Value = "Hello World";
    }

    // From @JeffreyZhao:
    //
    // Use a static method to trigger the static constructor automatically,
    // or we need to use RuntimeHelpers.RunClassConstructor to make sure
    // DefaultData is corrected initialized.
    //
    // The usage of RuntimeHelpers.RunClassConstructor is kept but commented.
    // Using GetDefault<T>() is a better approach since static Foo() would be
    // called multiple times for different generic arguments (although there's 
    // no side affect in this case).
    //
    // Thanks to @mikez for the suggestion.
    public static T GetDefault<T>()
    {
        return DefaultData<T>.Value;
    }
}

public class Foo<T>
{
    /* See the comments above.
    static Foo()
    {
        RuntimeHelpers.RunClassConstructor(typeof(FooHelper).TypeHandle);
    }
     */

    public T Data { get; protected set }

    public Foo()
    {
        Data = FooHelper.GetDefault<T>();
    }
}

You could specify the default values for limited types, and the result of them would be kept to default values.

This trick has several variations in practice. In my project, we use a generic ITypeConverter<T> instead of the build in TypeConverter to avoid unnecessary boxing:

public interface ITypeConverter<T>
{
    bool CanConvertTo<TTarget>();
    TTarget ConvertTo(T value);
}

The same trick could be applied as:

public class LongConverter : ITypeConverter<long>
{
    private static class Op<TTarget>
    {
        public static Func<long, TTarget> ConvertTo;
    }

    static LongConverter()
    {
        Op<string>.ConvertTo = v => v.ToString();
        Op<DateTime>.ConvertTo = v => new DateTime(v);
        Op<int>.ConvertTo = v => (int)v;
    }

    public TTarget ConvertTo<TTarget>(T value)
    {
        return Op<TTarget>.ConvertTo(value);
    }
}

Elegant, fast and clean.