-4

Is it possible to assign a variable to an expression in C# so that I don't have to write a method for it? For example, a variable named canBuild be equal to an expression "numResources > 50". And whenever that variable is called, it recalculates it's value. I've tried looking into lambdas but I don't really understand the documentation.

Thank you

EDIT:

Here's an attempt I tried with using lamdas:

public void setStateFloat(float variable, float comparison, int compareType)
{
    Func<bool> stateEval;
    switch(compareType)
    {
        case 0:
            stateEval = () => variable < comparison;
            break;
        case 1:
            stateEval = () => variable > comparison;
            break;
        case 2:
            stateEval = () => variable == comparison;
            break;
    }
}

Will this work if variable and comparison are not local to the object using this function (as in an object referring to another objects variable). I doubt it since it will only be a copy of the variable and not a pointer/reference to the where the original variable is stored.

Also, if I wanted to store a reference to the function, how would I do that?

The reason behind this is that I am developing a highly structured and modular system for designers and one of the requirements is creating a function from their input that are calculated initially and then used during runtime. Alternative approaches are welcome.

Kind thanks.

Sammi3
  • 367
  • 2
  • 5
  • 16
  • 1
    What didn't you understand about the documentation you saw on the subject? – Servy Jan 08 '19 at 22:17
  • Why don't you want to write a method? – Anderson Pimentel Jan 08 '19 at 22:18
  • 3
    Can you give us a code snippet of how you expect to use it? If I had a CanBuild property somewhere I'd just assign it a public get that's basically that expression. I'm not sure what you're trying to do. – Nikki9696 Jan 08 '19 at 22:19
  • 2
    This is what properties are for. – Wiktor Zychla Jan 08 '19 at 22:22
  • `public bool CanBuild => _numResources > 50;` – Rufus L Jan 08 '19 at 22:44
  • Your attempt with lambdas creates a closure over the parameters of the current method. You are right in stating that there will be copies, but this is only due to the way these parameters are passed (primitive types are passed by value by default). Unfortunately, this cannot be fixed with `ref` parameters, because those are not allowed inside lambda expressions / anonymous functions. – mbj Jan 09 '19 at 19:00
  • I think my main issue is how I've designed the solution to the task. I probably need to go back to the drawing board and work on the design more in order to implement what I'm trying to do. My initial thoughts is creating a class which has methods for the comparisons instead of trying to create functions. The class will have variables for the object that it needs the variable from and a string of the variable. Then use reflection to get the value of the variable. – Sammi3 Jan 09 '19 at 19:06

2 Answers2

5

You won't be able to completely avoid the notion of a method, but C# lambda expressions offers a nice clean syntax for creating anonymous functions that can be used the way you're suggesting. In your example, the expression has no input parameters, and would therefore look like this:

() => numResources > 50

Here, numResources refers to a variable that is declared elsewhere in your code, but visible in the scope where the anonymous function is created. This results in a closure over numResources, which in C# means that the anonymous function can access that variable at any time, even if the function is invoked from a location where numResources isn't normally available.

Now, in order to assign the anonymous function to a variable so that it can be evaluated multiple times or passed around between methods, a delegate type must be used. These days, delegates are usually declared using one of the Func<TResult,...> or Action<...> types.

Since your example expression has no parameters and returns a bool, you would declare the variable as being of type Func<bool>. Combining declaration and initialization in a single statement, this becomes:

Func<bool> canBuild = () => numResources > 50;

Note that the type needs to be explicitly declared, var won't work here. Now you have a variable canBuild, which you can use just as if it was the name of a method:

bool result = canBuild();

Example

// Declare and initialize numResources
int numResources = 42;

// Declare and initialize a delegate and the anonymous function
Func<bool> canBuild = () => numResources > 50;

// Invoke the function    
bool result = canBuild();  // False this time, since 42 < 50

// Update numResources
numResources = 75;

// Invoke the function again 
bool result = canBuild();  // True this time

Example with closure over local variable

The following example demonstrates how C# closures make variables become shared, so that the anonymous function is actually referencing the original variable, and not just a copy:

    class Program
    {
        public static void CreateAnonFuncs(out Func<int> getNumber, 
                                           out Action<int> setNumber)
        {
            // Declare a local variable
            int localNum = 42;

            // Create anonymous functions that reference the local variable
            getNumber = () => localNum;
            setNumber = (n) => { localNum = n; };

            // Let's update the local variable afterwards
            localNum = 21;
        }

        public static void Main()
        {
            // Call the method that creates the functions
            CreateAnonFuncs(out var getter, out var setter);

            // Now invoke the functions to prove that the variable is shared
            Console.WriteLine($"Value from getter: {getter()}"); // 21 (not 42)
            setter(99);
            Console.WriteLine($"New value from getter: {getter()}"); // 99 (not 21)
        }
    }
mbj
  • 987
  • 5
  • 18
  • I think OP was looking for something that isn't a method, but more like a readonly property. Perhaps `bool canBuild => numResources > 50;` – idream1nC0de Jan 08 '19 at 22:24
  • @yurish: Am I correct in thinking that the reason the compiler won't infer an implicit type for lambdas might be that the lambda syntax can be used for not only Func, but also Expression, etc? – mbj Jan 08 '19 at 22:40
  • 1
    @mbj, yes, there is an answer for this: https://stackoverflow.com/questions/4965576 – yurish Jan 08 '19 at 22:45
  • Thank you for the replies. Sorry about the issues in the post. This was what I was looking for. Last question, is it possible to store a reference to the delegate in another variable. So that I can go through a list of these delegates and execute the sequentially? – Sammi3 Jan 09 '19 at 16:25
  • @Sammi3: Sure, you can use the "value" (i.e. the delegate reference) of `canBuild` just like any other variable, e.g. store it in a list (e.g. `List>`), pass it to some other method, etc. and then invoke it using whatever variable/parameter name you have assigned it to. Since you mentioned executing multiple delegates, you might also want to have a look at the [Delegate.Combine](https://learn.microsoft.com/en-us/dotnet/api/system.delegate.combine) method. – mbj Jan 09 '19 at 17:31
  • Thank you again. Last thing, can a delegate use a variable in another object. I tried passing the object into a function that creates the delegate and using the this.GetType().GetField(variablename).GetValue(this); but it just threw errors. Is there a simpler way to achieve what I am trying to do? – Sammi3 Jan 09 '19 at 18:58
1

I'm not sure what are you trying to achieve, but you could take a look at properties with getter implementation, like so:

public class Test 
{
   private int _numResources;
   public bool CanBuild 
   { 
      get
      {
        return _numResources == 50;
      }
   }
}
  • Or simply: `public bool CanBuild => _numResources > 50;` – Rufus L Jan 08 '19 at 22:42
  • This does not provide what the OP asked for exactly, but this is most likely the best approach. `CanBuild` appears to be most suited for a read-only property with a calculated value. – Rufus L Jan 08 '19 at 22:44
  • Yeah, functionally it's the same, but as he said he's not up to date with lambdas so I wanted to show him the basic concept. – Kamil Kuryś Jan 08 '19 at 22:44