-1

I am working on a project in which I need to define any number of possible parameters when creating an object.

public class base
{
    //Variable Declarations here...
    
    public base(){ //Handle Arguments here... }
}

public class customCommand : base
{
    //Action Logic here...
}

My main issue comes from the declarations of "customCommand" (sorry for the poor naming conventions.) If the user wants to create an object that passes no further parameters, then the declaration works perfectly. However, If the user wants to create an object that does pass a parameter, I need to handle this with a new declaration. The purpose of the script is to allow the user to create a command for a command-prompt-esc simulator. I have an 'interpreter' that will pass any parameters into the method that is associated with the command. My interpreter already "handles" type safety by returning whatever error the assigned function returns in a formatted way to the user. If the user needs to type "command1 [string] [int]" but they type "command1 [string] [string]", for example, the interpreter will see that the function assigned returned a type-error along with the type needed, and tell the user to try the command again with the correct syntax.

I would define command1 by creating an object like this: private static customCommand<string,int> command1; and I would define the method the command uses (for the interpreter) like this:

command1 = new customCommand<string,int>((x,y) =>
{
//some method
});
public class customCommand<T1> : base
{
    //New Action Logic here (handles T1)...
}

I understand one solution to this is simply limiting the user's ability to create an object with 'n' number of parameters, and writing out new definitions of new with <T1> * n parameters each, like:

public class customCommand<T1> : base { }
public class customCommand<T1,T2> : base { }
public class customCommand<T1,T2,T3> : base { } etc...

however, the possible inputs the user would have could exceed 10-11 entries, which would not only be a pain to manage in 10-11 different declarations but would also serve a messy solution.

I tried creating a class with an empty list of parameters. I expected to be able to create an object in which I could pass any number of parameters I needed.

public class customCommand<new T[]> { };
private static customCommand<string, string, int> test; //parameters are just an example

Any help would be appreciated, I am still learning. Thank you! (sorry for poor formatting, im trying to edit the post for the comments while running be

22
  • 6
    "Is there a way to make a class with no limit on the number of Generic Type Parameters" No. If you're making a method with, say, more than 3 generic arguments, you're probably going about things the wrong way. This is a good sign of a X/Y problem.
    – gunr2171
    Commented Apr 24 at 12:57
  • 6
    I need to define any number of possible parameters why? What's the actual problem that would solve? Why use even 3 types instead of just 1 with 3 properties? If the class is supposed to do something, why not use Action<> or Func<> ? Commented Apr 24 at 12:59
  • 3
    @topsail params is only for method calls Commented Apr 24 at 13:00
  • 2
    The "why" matters. In functional language where eg tuples of different types are common, arbitrary types are handled by nesting tuples, not by having infinite parameters. Recursive functional code that handles 2 types can handle an infinite number because the second type can itself be a tuple. It's easy to write such code in C# and use tricks borrowed from functional languages like memoization to simplify the code and reduce or even eliminate the cost of recursion Commented Apr 24 at 13:02
  • 1
    @PanagiotisKanavos well, it may be part of the answer. As it seems OP uses generics for providing data to a class, which isn't really what generics are for. Commented Apr 24 at 13:10

2 Answers 2

3

I want the user to be able to create an object with any number of parameters. Then, I want to take every parameter, and pass them into my interpreter script. I am making a modular command-prompt-esc program, and I want the user to be able to create their own commands that can be accessed, and understood by my interpreter.

I would suggest thinking instead of a single argument that might represent multiple values; options that leap to mind:

// dictionary
var val = new Dictionary<string, object>
{
    { "Id", 42 },
    { "Name", "abc" },
};
SomeMethod(val);

// anonymous type, using reflection magic in your library
var val = new { Id = 42, Name = "abc" };
SomeMethod(val);

// named value-tuple; note names aren't available via reflection,
// but are via "analyzers"
var val = (id: 42, name: "abc");
SomeMethod(val);

// unnamed value-tuple
var val = (42, "abc");
SomeMethod(val);

// POCO type, using reflection magic in your library
var val = new SomeType { Id = 42, Name = "abc" };
SomeMethod(val);

and those are just where your library does the tearing apart; another approach is to have some pair of types - for example IParameterHandler<T> i.e. for arg type Foo you might request an IParameterHandler<Foo> that the caller provides, which does this in the caller's code, extracting some number of typed arguments.

1

Imagine there was a way to define such a class:

class MyClass<T1, ..., Tn> { ... }

How would you ever use this class? Imagine you'd wanted to put it into a function. How would the functions signature looks like?

DoSomething(MyClass<hmmmm, what arguments do I need here? T1? T2? T38?> args) { ... }

But even if that would work, how would you use that object in the method? All you could potentially do is iterating all these values. However you have no clue about what all these types actually are, so what does T38 resolve to? And does args even have so many arguments? E.g. how would you write such a loop:

foreach((hmmm, which type do I need here? T1?, T30? Both?) m in args) 
{
    ... 
}

You're stuck on indicating every single object as object, in which case you completely lose all the type-safety you actually wanted.

foreach(object m in args) 
{
    ... 
}

Even the class itself cannot really use these arguments. I suppose you want to use these values as some data-storage:

class MyClass<T1, ..., Tn>
{
    public T1 FirstValue;
    public T2 SecondValiue;
    // how many properties do I actually have? 
}

However when dealing with any arbitrary number of type-arguments, how would the class itself know, "how many properties it needs to create"?

So after all generics - which give you compile-time-types aren't the correct thing when you want to interpret the types at runtime.

What you can do instead is just baking all these values into a collection, e.g. a List<object> anf iterate that in your interepreter:

class MyClass 
{
    public object[] Values { get; }
    public MyClass(params object[] args) { this.Values = args; }
}

Now you can provide any arbitrary number of args to the class:

var m = new MyClass("Hello", 2, new MyClass());

Your interepreter just needs to iterate the args-array:

foreach(var o in m.Values)
{
   ...
}
1
  • I see how a theoretical 'infinite' number of potential arguments would be problematic. I couldn't find any industry documentation that would support the idea that command lines have a limit to arguments, but looking back, I suppose it makes sense. Addressing the solution, while my interpreter does handle the types that the arguments pass in the same way any other interpreter could, I do thing handling the types of the objects before sending them to the interpreter would be helpful for errors/outputs. Like you said, I will put the args in a list and rework the interpreter. Thank you!
    – Zacc
    Commented Apr 24 at 13:54

Not the answer you're looking for? Browse other questions tagged or ask your own question.