IIF function, how to make it take 2N + 1 arguments from logical expressions?, C#

Question:

In Visual Basic there is this IIF Function , as in Crystal Report, etc…

In C# itself there is no such function, but it is the same as doing something like this:

bool a = true;
string b = a ? "Verdadero" : "Falso";

But to make the code a little easier to read, I wanted to do it as a C# function, looking like this:

public static T IIf<T>(bool Condicion, T ParteVerdadera, T ParteFalsa) 
{
     return Condicion ? ParteVerdadera : ParteFalsa;
}

Or so that it doesn't operate with the actual values, it can also be done using delegates, to access the necessary values:

public static T IIf<T>(bool Condicion, Func<T> ParteVerdadera, Func<T> ParteFalsa) 
{
    return Condicion ? ParteVerdadera() : ParteFalsa();
}

So far this works fine…


But how can I modify this function so that it can take 2N + 1 arguments?

(N – the number of logical expressions specified)

Example the desired result:

int valor = IIf(Nombre = "Joel", 1, Nombre = "Pedro", 2, Nombre = "Maria", 3, 4);

Can someone give me a hand with this?

Environment: C# – Visual Studio 2017

Answer:

I'm going to answer my own question just in case someone else is interested, based on answers provided to me in SO English, although I found the expected result, I practically chose to abandon this idea, because it was considered a bad practice.

Method 1:

If you want to preserve the order of the parameters, you can do something like this:

public T IIf<T>(params object[] objects) 
{
    for(var i = 0; i < objects.Length - 1; i += 2) 
        if((bool)objects[i])
           return (T)objects[i+1];

    return (T)objects[objects.Length - 1];
}

The type you are working with must be explicitly declared. So you would have to use it like this:

int valor = IIf<int>(Nombre = "Joel", 1, Nombre = "Pedro", 2, Nombre = "Maria", 3, 4);

If we look at passing int as a type parameter. This can be avoided by changing the order of the parameters so that the default value comes first.

But, if we're just willing to adopt a personal pattern, the nested ternary syntax can be quite readable:

int valor = 
      Nombre == "Joel" ? 1
    : Nombre == "Pedro" ? 2
    : Nombre == "Maria" ? 3
    : 4;

Method 2:

First of all, from the feedback I got, this is a bad idea because newer versions of C# already support pattern-matching switches as a built-in feature of the language.

Second, this is a bad idea because the "argument, case1, result1, case2, result2, …" API has a signature that is difficult to express in the C# type system.

But in the same way, if it were mandatory to implement said API, I would suggest using tuples:

public static R Switch<A, R>(A item, R theDefault, params (A, R)[] cases )
{
    foreach(var c in cases) 
        if (item.Equals(c.Item1))
            return c.Item2;
    return theDefault;
}

Or, make a more useful method:

public static T FirstOrDefault(this IEnumerable<T> items, T theDefault, Func<T, bool> predicate)
{
    foreach(var i in items.Where(predicate))
      return i;
    return theDefault;
} 

public static R Switch<A, R>(A item, R theDefault, params (A, R)[] cases ) =>
      cases.FirstOrDefault(
          (item, theDefault), 
           c => item.Equals(c.Item1)).Item2;

If you can't use tuples because you're using an older version of C#, you can create your own pair type or use the key-value pair type.

Scroll to Top