views:

219

answers:

4

In C# I am trying to write code where I would be creating a Func delegate which is in itself generic. For example the following (non-Generic) delegate is returning an arbitrary string:

Func<string> getString = () => "Hello!";

I on the other hand want to create a generic which acts similarly to generic methods. For example if I want a generic Func to return default(T) for a type T. I would imagine that I write code as follows:

Func<T><T> getDefaultObject = <T>() => default(T);

Then I would use it as

getDefaultObject<string>() which would return null and if I were to write getDefaultObject<int>() would return 0.

This question is not merely an academic excercise. I have found numerous places where I could have used this but I cannot get the syntax right. Is this possible? Are there any libraries which provide this sort of functionality?

A: 

You can't do this, because generic type parameters have to be known at runtime. You have to use the activator class:

Object o = Activator.CreateInstance(typeof(StringBuilder));

which will do exactly what you want to. You can write it as the following:

public T Default<T>()
{
  return (T)Activator.CreateInstance(typeof(T));
}

Edit

Blindy's solution is better.

Femaref
+7  A: 

Well you can't overload anything based only on the return value, so this includes variables.

You can however get rid of that lambda expression and write a real function:

T getDefaultObject<T>() { return default(T); }

and then you call it exactly like you want:

int i=getDefaultObject<int>();       // i=0
string s=getDefaultObject<string>(); // s=null
Blindy
Thanks for the answer Blindy, but the question was more about dynamically setting a Generic Generic Delegate (if that is at all possible). The problem with your code about is that you are creating a method rather than a delegate which can declared, instantiated and used WITHIN a method.
Tahir Hassan
True, but doesn't answer the question.
Dario
A: 

This isn't possible, since a delegate instance in C# cannot have generic parameters. The closest you can get is to pass the type object as a regular parameter and use reflection. :(

In many cases, casting to dynamic helps remove the pain of reflection, but dynamic doesn't help when creating new instances, such as your example.

Stephen Cleary
+4  A: 

Though one might find practical workarounds like Stephen Cleary's

Func<T> CreateGetDefaultObject<T>() { return () => default(T); }

where you can specify the generics directly, this is a quite interesting problem from a theoretical point that cannot be solved by C#'s current type system.


A type which, as you call it, is in itself generic, is referred to as a higher-rank type.

Consider the following example (pseudo-C#):

Tuple<int[], string[]> Test(Func<?> f) {
    return (f(1), f("Hello"));
} 

In your proposed system, a call could look like that:

Test(x => new[] { x }); // Returns ({ 1 }, { "Hello" })

But the question is: How do we type the function Test and it's argument f? Apparently, f maps every type T to an array T[] of this type. So maybe?

Tuple<int[], string[]> Test<T>(Func<T, T[]> f) {
    return (f(1), f("Hello"));
} 

But this doesn't work. We can't parameterize Test with any particular T, since f should can be applied to all types T. At this point, C#'s type system can't go further.

What we needed was a notation like

Tuple<int[], string[]> Test(forall T : Func<T, T[]> f) {
    return (f(1), f("Hello"));
} 

In your case, you could type

forall T : Func<T> getDefaultValue = ...

The only language I know that supports this kind of generics is Haskell:

test :: (forall t . t -> [t]) -> (Int, String)

See this Haskellwiki entry on polymorphism about this forall notation.

Dario
Thanks Dario, very interesting answer. Although I ain't a Haskell master by any stretch, I understood your answer and the link you gave. As far as I understand, there is no solution/workaround as this is a limitation of the language as it does not support Rank N Types. Perhaps in another 5 - 10 years time Microsoft will add "Rank n Types" to the CLR.
Tahir Hassan
@tahirhassan: Yep, it's a quite fundamental property of the language's type system (and a quite advanced one - even Haskell does only support it by non-standard extensions) that one can't outrun. One possibility I see though it that you can sort-of emulate this behaviour by implementing an interface like `interface ToRankNArray { T[] Apply<T>(T arg); }`. Languages like Java or F# allow the creation of anonymous subtypes, but of course it's neither as convenient as an anonymous function nor does it change the type-system itself.
Dario