0

The method HasInvalidInputs accepts the tuple with keys and values, herewith the number of pairs, and also keys names are unknown at advance.

public class ValidatableControlsGroup
{

  public static bool HasInvalidInputs(object /* TODO specify more exact type */ controlsPayload)
  {
    // TODO implement
    return true;
  }
}

If it was the TypeScript, in above code it will be { [key: string]: ValidatableControl.Payload } instead of object. The usage of ValidatableControlsGroup.Payload could be:

public partial class TaskManager : ComponentBase
{
  private (ValidatableControl.Payload taskTitle, ValidatableControl.Payload taskDescription) controlsPayload = (
    taskTitle: new ValidatableControl.Payload(initialValue: "", new TaskTitleInputtedDataValidation()),
    taskDescription: new ValidatableControl.Payload(initialValue: "", new TaskTitleInputtedDataValidation())
  );
    
  private void updateTask()
  {

    if (ValidatableControlsGroup.HasInvalidInputs(this.controlsPayload))
    {
      ValidatableControlsGroup.PointOutValidationErrors(this.controlsPayload);
      return;
    }


    this.targetTask.Title = this.controlsPayload.taskTitle.GetExpectedToBeValidValue();
    this.targetTask.Description = this.controlsPayload.taskDescription.GetExpectedToBeValidValue();

    // ...

  }
}

Inside TaskManager we need to access to each ValidatableControl.Payload by key that is why I want to use the tuple with keys. If it was the TypeScript, the type of controlsPayload was { taskTitle: ValidatableControl.Payload; taskDescription: ValidatableControl.Payload } which is compatible with generalized { [key: string]: ValidatableControl.Payload } of parameter of ValidatableControlsGroup.HasInvalidInputs.

Please note that the enumerating like if (ValidatableControlsGroup.HasInvalidInputs(item1, item2, item3, item4)) is not allowed because the ValidatableControlsGroup is being developed as library class thus it take care about routines like extraction of the elements from the tuple.

Lastly, in my case the HasInvalidInputs method needs only values, but not keys. However, if the transforming to array required, the HasInvalidInputs as the library class must take care about this routine.

Regarding my case HasInvalidInputs method does not need the keys - it is enough the values. However, the keys are required in TaskManager. If will set the types of controlsPayload parameter to ValidatableControl.Payload[], the library user will be forced to transform the tuple to array that is unacceptable for library class because the library class must take care about such routines.

Takeshi Tokugawa YD
  • 670
  • 5
  • 40
  • 124
  • Are you asking how to handle a group of some unknown number of values, of arbitrary types? If you have varying number of values you need an array, or other collection. If you want to handle arbitrary types at runtime you need `object`. But I'm not fluent in type script, so I have some difficulty understanding what your actual goal is. – JonasH May 24 '23 at 11:38
  • @JonasH Thank you for the comment. "Are you asking how to handle a group of some unknown number of values, of arbitrary types?" - Not exactly. It will be tuple with keys and values, herewith the type of value is of `ValidatableControl.Payload` type (the class which code does not matter for this question) : `(key1 ValidatableControl.Payload, key2 ValidatableControl.Payload, ..., keyN ValidatableControl.Payload)`. What we don't know at advance inside `HasInvalidInputs` method are the keys' names and number keys + values pairs. – Takeshi Tokugawa YD May 25 '23 at 10:46
  • @JonasH, "If you have varying number of values you need an array, or other collection. " - The requirement of my case is accessing to element by keys like `controlsPayload.taskTitle`, but only inside the `TaskManager` class. The Dictionary will not prevent the error if I'll request some not existing value, while tuple will. – Takeshi Tokugawa YD May 25 '23 at 10:51

2 Answers2

2

You could use generics:

public void Foo<TKey, TValue>((TKey Key, TValue Value) tuple)
{
    Console.WriteLine(tuple.Key);
}

var f = ("foo", "bar");
Foo(f);

However, opinion time:

ValidatableControlsGroup is being developed as library class thus it take care about routines like extraction of the elements from the tuple.

Then just define an interface and use that for the method parameter type. Value tuples should not be exposed from or accepted by library code. They should be used to cross one boundary at most, preferably within the same assembly. But that's IMHO.

CodeCaster
  • 147,647
  • 23
  • 218
  • 272
  • Count me in. Its rather a IOHO. – Ralf May 24 '23 at 11:11
  • Thank you for the answer. I am sorry, but the `(TKey Key, TValue Value)` type is completely unrelated with my case. In my case the tuple has `(ValidatableControl.Payload taskTitle, ValidatableControl.Payload taskDescription)` schema where `taskTitle` and `taskDescription` are keys and values are always of `ValidatableControl.Payload` type. Inside the `HasInvalidInputs` we know that it will be the tuple with keys and values of `ValidatableControl.Payload` type, however we don't know at advance the tuple keys and also the key + value pairs number. – Takeshi Tokugawa YD May 25 '23 at 10:32
  • @Takeshi why don't you create a [mre] that more clearly explains your use case then? I also really don't get what you mean by "keys". They're tuples. Finally, I don't get what you mean by the number. You want a `params Something[]` parameter? – CodeCaster May 25 '23 at 10:35
  • Because of lack of time. What are want to see in the minimal reproducible example additionally to code that I posted? – Takeshi Tokugawa YD May 25 '23 at 10:42
  • @Takeshi in `HasInvalidInputs(TBD controlsPayload) { }`, what _exactly_ do you want to do with `controlsPayload`, and why can't you do that using the generic syntax I provided? – CodeCaster May 25 '23 at 12:16
  • @CodeCaster "what exactly do you want to do with `controlsPayload`" - To iterate the values of this tuple. Inside `HasInvalidInputs`, we don't need to know the keys while inside `TaskManager` - do. – Takeshi Tokugawa YD May 27 '23 at 02:15
  • @Takeshi you keep saying "keys" and "values" but you mean items? So you want to use Item1, Item2, ... ItemN? Why are you using a tuple instead of an array for that? See https://stackoverflow.com/questions/43341177/how-to-iterate-over-tuple-items#43341805 – CodeCaster May 27 '23 at 06:06
  • @CodeCaster AFAIK when being said "Tuple" means the linear array-like entity of pre-defined elements count like `(item1, item2, item3)`. However there are the "keyed" tuples in C# like `(string firstName, string lastName, byte age)` where `firstName`, `lastName` and `age` are keys. Some specific name named for this kind of tuple? Anyway there are at least 3 important differences with `Dictionary` which I need inside `TaskManager`: 1) If non-existing key has been accessed, it will be the C# error. 2) Non-null elements guarantee. 3) Code autocompletion in the editors and IDEs. – Takeshi Tokugawa YD May 27 '23 at 06:34
  • @CodeCaster Inside the `HasInvalidInputs` the keys are not require anymore, it is enough just to iterate the values (everyone of `ValidatableControl.Payload` type). The tuple with keys and values could not be simply iterated, however being the library class, `ValidatableControlsGroup` must take care about converting routine of the tuple with keys and values to something which easier to iterate. – Takeshi Tokugawa YD May 27 '23 at 06:37
  • 1
    @Takeshi there's your misconception, a tuple is not a linear array-like entity. It's more akin to a struct or an anonymous type. A tuple with named items is called a tuple with field names. If you want to enumerate its fields/properties/items, you're gonna need reflection. – CodeCaster May 27 '23 at 06:48
  • @CodeCaster You mean `GetFields`/`GetValue`? I implemented as `bool HasInvalidInputs(object controlsPayload) { Type parameterType = controlsPayload.GetType(); foreach (FieldInfo fieldInfo in parameterType.GetFields()) { /* */ } }`. – Takeshi Tokugawa YD May 27 '23 at 07:04
  • 1
    @Takeshi yeah that would work! – CodeCaster May 27 '23 at 07:18
2

The fact is the members of the ValueTuple family are all structs. So if you need a type to represent them, object seems to be the only choice.

Another approach is adding a bunch of overloaded methods to cover the tuple types you may pass in, and then convert them to arrays.

public static bool HasInvalidInputs(ValueTuple<ValidatableControl.Payload> controlsPayload)
    => HasInvalidInputs(controlsPayload.Item1);

public static bool HasInvalidInputs(ValueTuple<ValidatableControl.Payload, ValidatableControl.Payload> controlsPayload)
    => HasInvalidInputs(controlsPayload.Item1, controlsPayload.Item2);

......

private static bool HasInvalidInputs(params object[] controlsPayload)
{
}

In a word, your issue is that you cannot find a suitable parameter type for this method. Perhaps you should reconsider the interface.

shingo
  • 18,436
  • 5
  • 23
  • 42
  • Thank you for the answer. Do I get it right that in this case the signature could be only `bool HasInvalidInputs(object controlsPayload)`? Because the key + value pairs number could be arbitrary large, the overloading does not seem to be the solution in this case. – Takeshi Tokugawa YD May 25 '23 at 10:38
  • Yes, because they are value types. Also for this reason, the key + value pairs number should not be very large, the general [recommended value is 16 bytes](https://stackoverflow.com/questions/64816714/when-to-use-record-vs-class-vs-struct), which is 2~4 references. If you do need to pass in more than 4 values, I suggest using a dictionary. – shingo May 25 '23 at 12:19