Using the "Params" Keyword for Generic Parameters in C#

Using the params keyword for generic parameters in 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.)

Using C# params keyword in a constructor of generic types

A clearer solution would be to have two static factory methods. If you put these into a nongeneric class, you can also benefit from type inference:

public static class Houses
{
public static Houses<T> CreateFromElements<T>(params T[] initialElements)
{
return new Houses<T>(initialElements);
}

public Houses<T> CreateFromDefault<T>(int count, T defaultValue)
{
return new Houses<T>(count, defaultValue);
}
}

Example of calling:

Houses<string> x = Houses.CreateFromDefault(10, "hi");
Houses<int> y = Houses.CreateFromElements(20, 30, 40);

Then your generic type's constructor doesn't need the "params" bit, and there'll be no confusion.

C# method generic params parameter bug?

Seems to work for me, does the rest of your code look like the following?

class TestThing<T>
{
public void SomeMethod(string message, T data)
{
Console.WriteLine("first");
}
public void SomeMethod(string message, params T[] data)
{
Console.WriteLine("second");
}
}

class Program
{
static void Main(string[] args)
{
var item = new object();
var test_thing = new TestThing<object>();
test_thing.SomeMethod("woohoo", item);
test_thing.SomeMethod("woohoo", item, item);

Console.ReadLine();
}
}

Compiles fine on .NET 3.5 and outputs "first" and then "second" when run. Which version are you using/targeting?

EDIT

The issue from your code above is that the compiler can't tell what type null is. It is equally valid to assume that it is a string or an array of strings, hence why it is ambiguous when just null and not ambiguous when you specifically cast it (i.e. you're telling the compiler how it should be treated).

UPDATE

"The same can be argued for
SomeMethod(string, string) and
SomeMethod (string, params string[]),
yet it works"

Actually, no it doesn't. You get the same ambiguous method problem.

public static string TypedToString(string item1, string item2)
{
return "";
}

public static string TypedToString(string item1, params string[] items)
{
return "";
}

public static void CallToString()
{
TypedToString("someString", null); // FAIL
}

Invoking Generic method with params parameter through reflection

Try repleace new object[]{"param"} with new object[] { new[] { "param" } }.

Here new object[] is the array of parameters, and the first parameter should be a string[], but in your code, you use a string, hence the exception.

Is params Keyword supports with ValueTuple as a parameter C# 7.0?

A parameter array is a very specific language feature that is only applicable to method parameters. A value tuple is a struct with variable length generic parameters and has been given a special syntax for initialization and usage (most of which are just smoke an mirrors).

var asd = ("asd","asd");

Is basically just syntactic sugar for

ValueTuple<string, string> valueTuple = new ValueTuple<string, string>("asd", "asd");

There isn't even anything overly magical about named tuples.

var asd = (bob1 : "asd", bob2 : "asd");
Console.WriteLine(asd.bob1);

Basically converts to the following

ValueTuple<string, string> valueTuple = new ValueTuple<string, string>("asd", "asd");
Console.WriteLine(valueTuple.Item1);

So you are essentially asking, why you can't use params in a tuple initializer syntax.

Well, as discussed, because a value tuple initializer actually isn't a method, and as much as it looks like it, the arguments you give it are not a method parameters. Tuple syntax is it's own very special language feature. However, you do have options.

So let's look at what you are trying to achieve. If you had a method parameter as such

public void Test((string arg1, params string[] args) tuple)

the only advantage would be you could supply a comma separated list.

Test(("bob","args1","args2","args3"));

Since we can't, your options are

Test(("bob",new []{"args1","args2","args3"}));

Or with some tomfoolery you could use an explicit operator on your own struct.

public readonly struct Testing
{
public Testing(ITuple x)
{
CategoryName = (string) x[0];
Properties = EnumerateTuple<string>(x).ToArray();
}
public Testing(string categoryName,string[] properties)
{
CategoryName = categoryName;
Properties = properties;
}

private static IEnumerable<T> EnumerateTuple<T>(ITuple x)
{
for (var i = 1; i < x.Length; i++)
yield return (T) x[i];
}
public string CategoryName { get; }
public string[] Properties { get; }
public static implicit operator Testing((string, string[]) x) => new Testing(x.Item1,x.Item2);
public static implicit operator Testing(string x) => new Testing(x, Array.Empty<string>());
public static implicit operator Testing((string, string) x) => new Testing(x);
public static implicit operator Testing((string, string,string) x) => new Testing(x);
public static implicit operator Testing((string, string, string, string) x) => new Testing(x);
public static implicit operator Testing((string, string, string, string, string) x) => new Testing(x);
public static implicit operator Testing((string, string, string, string, string, string) x) => new Testing(x);
public static implicit operator Testing((string, string, string, string, string, string, string) x) => new Testing(x);
public static implicit operator Testing((string, string, string, string, string, string, string, string) x) => new Testing(x);
public static implicit operator Testing((string, string, string, string, string, string, string, string, string) x) => new Testing(x);
public static implicit operator Testing((string, string, string, string, string, string, string, string, string, string) x) => new Testing(x);
public static implicit operator Testing((string, string, string, string, string, string, string, string, string, string, string) x) => new Testing(x);
public static implicit operator Testing((string, string, string, string, string, string, string, string, string, string, string, string) x) => new Testing(x);
}

Which would allow shenanigan's such as this

public static void Test(Testing something)
{
Console.WriteLine(something.Category);
Console.WriteLine(string.Join(", ", something.Properties);
}

private static void Main(string[] args)
{
Test("asd");
Test(("asd"));
Test(("asd", "args1"));
Test(("asd", "args1", "args2"));
Test(("asd", new []{"args1", "args2"}));
}

Note : This was only for academic purposes, I really wouldn't expect anyone to want to do this

params keyword taking an array as a single parameter interprets the contents of the array as all its parameters

You can cast the argument to an object:

public static void Main()
{
Paramtest((object)new object[] { "hi", "wow", 78 });
Console.ReadKey();
}

Why use the params keyword?

With params you can call your method like this:

addTwoEach(1, 2, 3, 4, 5);

Without params, you can’t.

Additionally, you can call the method with an array as a parameter in both cases:

addTwoEach(new int[] { 1, 2, 3, 4, 5 });

That is, params allows you to use a shortcut when calling the method.

Unrelated, you can drastically shorten your method:

public static int addTwoEach(params int[] args)
{
return args.Sum() + 2 * args.Length;
}


Related Topics



Leave a reply



Submit