TechnicalTophat
TechnicalTophat

Reputation: 1725

How to access and create if statement variables C#

I'm trying to keep to the DRY principle in C#, and I was wondering if it's possible to access the argument checks in an if block. For example, would the below be possible?

if (foo == true || bar == false)
{
  if (check0)
  {
    //foo is true
  }
  if (!check1)
  {
    //bar is false
  }
}

I was also wondering if it's possible to do this, to keep the scope clean?:

if (var foo = runTimeAccessedVariable == "bar")
{
  //we now have the runtime generated variable.
}
//but it doesn't exist here

Upvotes: 3

Views: 665

Answers (4)

Dovydas Šopa
Dovydas Šopa

Reputation: 2300

You can actually. You can use (extension) methods like this:

// For first case.
public static void If(this Lazy<bool>[] checks, Predicate<Lazy<bool>[]> ifCondition, Action<Lazy<bool>[]> ifAction) {
    if (ifCondition(checks)) ifAction(checks);
}

// For second case.
public static void If(this object[] variables, Predicate<object[]> ifCondition, Action<object[]> ifAction) {
    if (ifCondition(variables)) ifAction(variables);
}

And use of it would look like this:

// First case.
new[] { new Lazy<bool>(() => foo == true), new Lazy<bool>(() => bar == false) }.
    If(checks => checks[0].Value || checks[1].Value, checks => {
    if (checks[0].Value) {
        //foo is true
    }
    if (!checks[1].Value) {
        //bar is false
    }
});

// Second case.
new[] { "bar" }.If(variables => (string)variables[0] == "bar", variables => {
    //we now have the runtime generated variable.
});

This will limit the scope of those temporary values. However, this does not provide any check for whether you actually using checks or variables in the right way. And it would be harder to debug if something goes wrong then simple if statement.

This could also be extended to accept Func instead of Action that your If method could return result as well. And it is possible to even add ElseIf and Else methods to chain with If method (to mimic all if statement behavior).

Upvotes: 0

Steve Cooper
Steve Cooper

Reputation: 21490

The scope and reuse can both be trivially solved by using a pair of brackets to define a new scope;

pulic void MyFunc() 
{

    // do stuff here in the scope of MyFunc

    {
        // create child scope with new scoping rules and declare control variables
        var fooTrue = foo == true;
        var barFalse = bar== false;
        if (fooTrue || barFalse)
        {
            if (fooTrue)
            {
                //foo is true
            }
            if (barFalse)
            {
                //bar is false
            }
        }
    }

   // stuff here cannot access fooTrue, barFalse.
}

This stops the variables from 'leaking out' of this part of the function. There is a small semantic difference because the var barFalse line isn't shortcut, but it seems from your example that you'll probably need to test it anyway so it shouldn't be a concern.

Upvotes: 2

InBetween
InBetween

Reputation: 32780

Answering you second question, no it is not possible to declare a variable in this way. The only similar alternative is to use { } to create your own local scope and declare the local variable before the if statement:

{
    bool foo;

    if (foo = runTimeAccessedVariable == "bar")
    {

    }
}

In any case I'm not really sure what the benefits of this approach are. If you are insde the if statement, you already know foo is true, what is the use of having the scoped local?

Concerning your first question, I'd just push back on the whole approach. You are basically using an if statement based on a boolean expression made up of two or more conditions. Then, once inside the if statement you are discerning between what boolean condition is met or not. Also, you could have the case of an uninitialized variable, because check1 is not guaranteed to run.

I'd rewrite that completely and by-pass the need of any locally scoped variables:

  1. Work is independent for each condition:

    if (foo)
    {
        //whatever
    }
    else if (bar)
    {
        //whatever
    }
    
  2. There is common work for both conditions:

    if (foo)
    {
        //whatever
    
        if (bar)
        {
            //whatever
        }
    }
    

    Both would be semantically equivalent to what you are proposing.

Upvotes: 0

Heinzi
Heinzi

Reputation: 172448

Nope, it's not possible. In fact, since you use the short-circuiting || operator, the second check might not even be evaluated.

Obviously, you can work around it as follows:

var fooIsTrue = (foo == true);
var barIsFalse = (bar == false);

if (fooIsTrue || barIsFalse)
{
    ...
}

Note that this changes the behaviour of your code, since barIsFalse is always evaluated.

Upvotes: 4

Related Questions