Using the "params" keyword for generic parameters in C#

C#Generics

C# Problem Overview


I came across the beautiful Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, TResult> delegate in C# .NET 4.5 today. I assume 16 was an arbitrary place to stop (what methods have more than 16 parameters?) but it got me thinking: is it possible in C# to specify that a generic type can have any number of type arguments? in a similar way that the params keyword for methods allows for any number of arguments for a method. Something like this:

public class MyInfiniteGenericType<params T[]> { ... }

where inside the class you could then access the type arguments by enumerating through them or using T[index] in the same way that params allows within methods.

I've never had a use for this personally, but the Func delegate would be a perfect place to use it. There would be no need for 16 different types of Func!

So my question is, can this be done in any way in C#, and if not is this a silly idea?

C# Solutions


Solution 1 - C#

> is it possible in C# to specify that a generic type can have any number of type arguments?

No, C# doesn't have anything like that I'm afraid.

Fundamentally Func<T> and Func<T1, T2> are entirely unrelated types as far as the CLR is concerned, and there's nothing like params to specify multiple type arguments.

As for its utility: I can see cases where it could be useful, but I suspect they're rare enough to mean the feature doesn't cross the "benefit/cost" threshold. (Note that it would almost certainly require CLR changes too.)

Solution 2 - C#

C++11 has the feature that you're essentially talking about. They call it variadic templates.

C# generics aren't quite like C++ templates, though, and would make it difficult to build quite the same thing.

In the C++ case, the templates are expanded at compile time into whichever concrete types are used. In the C# case, the type specification happens entirely at runtime. And the resulting IL would need to support the number of different types encountered.

Solution 3 - C#

No, this cannot be done.

It's not as simple as treating it as an array of types (a concept which doesn't even exist in C#). Consider Func - the number of type parameters must be the same as the number of parameters of the delegate's Invoke method. But how would the programmer express such a relation between type parameters and regular parameters?

However, this feature does exist in C++11 - variadic templates. Note that C++ doesn't allow accessing the individual type parameters using array syntax - instead, functions usually separate the first type parameter from the rest, and use recursive calls to unpack the rest.

Solution 4 - C#

I just found myself curious to see if anyone else had a use for this. I just wrote a generic composer to build composites from mixins assembled using Castle Dynamic Proxy.

I built the composer to support two mixins and was about to proceed on "copying and pasting and adjusting (bleh)" the two mixin composer into 15 more variations up to a 17 mixing composer (similar to Func<T> through Func<T1...T16, T>). But then I thought, wouldn't it be great if I could just define Composer<TComposer, TIComposite, params TIMixin[]>!

Sadly, I'm off to copy and paste and adjust away (the very activity that generics help us avoid).

Solution 5 - C#

I had similar type of problem. I needed params KeyValuePair<Base, Implementation>[] pairs, where Implementation was inherited of Base.

Initially, I thought about IDictionary<Type, Type>, but that would kill Generics usage here.

So, what I did was create an Interface:

public interface IMapping
{
    Type Key { get; }
    Type Value { get; }
}

And its implementation:

public class Mapping<Base, Implementation> : IMapping where Base : class where Implementation : class, Base
{
    public Type Key => typeof(Base);
    public Type Value => typeof(Implementation);

}

Created a method:

public static void Initialize(params IMapping[] mappings)
{
    foreach (var mapping in mappings)
    {
        //We got our mapping, let's map
    }
}

And used it as:

Initialize(
    new Mapping<IMainView, MainView>(),
    new Mapping<IDashboardView, DashboardView>()
);

Solution 6 - C#

No, it is not possible to have an arbitrary number of generic arguments. It's probably also a silly idea, because generic type parameters aren't resolved at runtime, they're resolved at compile time, and so iterating over them like that isn't at all an obvious thing to be doing.

You might also think that Tuple<,,,,,,,,> might be an obvious place for it, but the solution there is when you run out of generic arguments, you make the last one a Tuple<,,,,> for the remaining fields.

Solution 7 - C#

I don't know if this would be useful, but a workaround could be :

Func<List<object>>

You would have to manually convert each element to the respective data type. You can use this iteratively:

 Convert.ChangeType(obj, obj.GetType())//using System;

And you could have the set of variables you want.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionGeorge PowellView Question on Stackoverflow
Solution 1 - C#Jon SkeetView Answer on Stackoverflow
Solution 2 - C#sblomView Answer on Stackoverflow
Solution 3 - C#DanielView Answer on Stackoverflow
Solution 4 - C#Tyree JacksonView Answer on Stackoverflow
Solution 5 - C#Junaid A KView Answer on Stackoverflow
Solution 6 - C#MattWView Answer on Stackoverflow
Solution 7 - C#ApocaleoneView Answer on Stackoverflow