3

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
TechnicalTophat
  • 1,655
  • 1
  • 15
  • 37
  • Are there any object oriented languages that support this? – Stian Standahl Jun 08 '16 at 09:45
  • Please only ask one question per question. – Heinzi Jun 08 '16 at 09:46
  • OK apologies @Heinzi, they have both already been answered but I will remember that – TechnicalTophat Jun 08 '16 at 09:47
  • 2
    C++ allows assignment-expressions. They were more trouble than they were worth (people didn't spot the assignments, and spelling mistakes e.g. = instead of == did not cause errors) so they were not included in C# –  Jun 08 '16 at 09:47
  • The outer `if` is entirely unnecessary. Can you give a real example? – CodeCaster Jun 08 '16 at 09:54
  • @CodeCaster If you had a huge code block that you wanted to run for multiple checks (in order), but only certain parts could run if certain criteria were met, then this would be extremely helpful. It would also allow for a cleaner scope (out of the `if` statement), and wouldn't violate the DRY principle by running the check inside the if again. – TechnicalTophat Jun 08 '16 at 09:59
  • 2
    My advice would be to break up that code block into smaller method that return useful variables. – CodeCaster Jun 08 '16 at 10:00

4 Answers4

4

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.

Heinzi
  • 167,459
  • 57
  • 363
  • 519
2

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.

Steve Cooper
  • 20,542
  • 15
  • 71
  • 88
0

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.

InBetween
  • 32,319
  • 3
  • 50
  • 90
0

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).

Dovydas Šopa
  • 2,282
  • 8
  • 26
  • 34