2

I have a static method that is used for checking the argument...

require(myStringVariable);

If the value does not meet some requirements, I just want do display a message.

Is it possible to display also the name of the variable (or the expression) passed as the argument? (In C++, a macro with stringize operator would do the job. Is there any equivalent or other tool to do the same in C#?)

Update: I am not searching anything like nameof(myStringVariable). Actually, I would like to call the method also like:

require(bareFname + ".ext");

and if the expression would not pass the check, then I would to do inside the method something like

static void required(... theExpressionArgument)
{
    string value = evaluate theExpressionArgument;
    if (value.Length == 0)
    {
        Console.WriteLine("ERROR: Non empty value is required for the expression "
                          + theExpressionArgument);
    }
}
pepr
  • 20,112
  • 15
  • 76
  • 139
  • 5
    Do you mean `nameof(variable)`? – Sebastian Hofmann May 14 '18 at 19:49
  • 1
    Possible duplicate of [c# .NET 4 stringify member name similar to #define str(s) #s in C](https://stackoverflow.com/questions/10252020/c-sharp-net-4-stringify-member-name-similar-to-define-strs-s-in-c) – Broots Waymb May 14 '18 at 19:51
  • 2
    I don't think he does want `nameof` because, if he used the above example, but in `requrie` the parameter is called `static void require (string myStr)` it will output `myStr` not, `myStringVariable.` – AustinWBryan May 14 '18 at 20:20
  • @SebastianHofmann: No. I have updated the question to explain what I need. – pepr May 14 '18 at 20:32
  • @BrootsWaymb: Sorry, but I do not think it is a duplicate. At least, I cannot see the similarity. It may be the case that it is not possible to do anything like this in C#. – pepr May 14 '18 at 20:49

1 Answers1

2

Based on this answer, you could rewrite your method like this:

public static void RequireNotEmpty(Expression<Func<string>> lambda)
{
    // Get the passed strings value:
    string value = lambda.Compile().Invoke();

    // Run the check(s) on the value here:
    if (value.Length == 0)
    {
        // Get the name of the passed string:
        string parameterName = ((MemberExpression) lambda.Body).Member.Name;

        Console.WriteLine($"ERROR: Non empty value is required for the expression '{parameterName}'.");
    }
}

which can then be called like this:

string emptyString = "";
RequireNotEmpty(() => emptyString);

and writes

ERROR: Non empty value is required for the expression 'emptyString'.


Note that above code assumes that you only want to check for strings. If that's not the case, you could use the signature public static void RequireNotEmpty<T>(Expression<Func<T>> lambda) which will then work for any type T.

Also I renamed the method to something I find both more readable and more meaningful.


EDIT: Recommendation

After reading your comments, I figured this might be what you want:

public static class Checker
{
    private static T GetValue<T>(Expression<Func<T>> lambda)
    {
        return lambda.Compile().Invoke();
    }

    private static string GetParameterName<T>(Expression<Func<T>> lambda)
    {
        return ((MemberExpression) lambda.Body).Member.Name;
    }
   
    private static void OnViolation(string message)
    {
        // Throw an exception, write to a log or the console etc...
        Console.WriteLine(message);
    }

    // Here come the "check"'s and "require"'s as specified in the guideline documents, e.g.

    public static void RequireNotEmpty(Expression<Func<string>> lambda)
    {
        if(GetValue(lambda).Length == 0)
        {
            OnViolation($"Non empty value is required for '{GetParameterName(lambda)}'.");
        }
    }

    public static void RequireNotNull<T>(Expression<Func<T>> lambda) where T : class
    {
        if(GetValue(lambda) == null)
        {
            OnViolation($"Non null value is required for '{GetParameterName(lambda)}'.");
        }
    }

    ...
}

And now you can utilise the Checker class like this:

public string DoStuff(Foo fooObj, string bar)
{
    Checker.RequireNotNull(() => fooObj);
    Checker.RequireNotEmpty(() => bar);

    // Now that you checked the preconditions, continue with the actual logic.
    ...
}

Now, when you call DoStuff with an invalid parameter, e.g.

DoStuff(new Foo(), "");

the message

Non empty value is required for 'bar'.

is written to the console.

Community
  • 1
  • 1
Thomas Flinkow
  • 4,845
  • 5
  • 29
  • 65
  • 1
    I see. To summarize, the only way to get a string form of the expression into the method is to make it an not-compiled lambda expression body. Then--inside the method body--I can compile the passed representation and call it to get the value. The same way, I can still access the string representation. (There is a missing open parenthesis before the `MemberExpression`.) Is there any better way to convert `lambda.Body` to the string that was written outside? Think about writing `RequireNotEmpty(() => myEmptyVariable + "");` – pepr May 14 '18 at 21:56
  • 1
    @pepr *the only way* - well, there exists a way to not use costly expressions but pass a `Func` instead, but it relies on IL parsing which works only for simple references (see the Reflect.cs file in https://code.google.com/archive/p/lokad-sdk/source/default/source). I'm not sure why you would want to add something to the string variable, but I would love to help you further. Can you maybe tell me more on what you want to achieve? Also, thanks for the accepted answer and thanks for mentioning the typo, I fixed it just now. – Thomas Flinkow May 14 '18 at 22:03
  • 1
    The solution is perfect for me. I really want to pass only one variable. I was just curious what is possible and what is not. Thank you. ;) – pepr May 14 '18 at 22:14
  • Basically, I have two functions `check()` and `require()` called at the beginning of another method to check argument values. There will be more methods like this (the outer ones) for processing a stream of data. And the passed parameter values (fed by the stream parser) should follow the Guideline document. (Type checking at compile time is not enough.) If the `check` or `require` fails, I just want to see it (or log it somehow in future), including the place where it failed. This is the intention. – pepr May 15 '18 at 17:22
  • 1
    @pepr I don't know what the Guideline document states, but I edited my answer and added a design that I recommend for your `check`'s and `require`'s. Roughly following that design, you can easily create reusable parameter checks. I hope I understand correctly and this is what you want to achieve. – Thomas Flinkow May 16 '18 at 09:22
  • 1
    This is nice. Thank you. :) Still, it is a pitty that C# does not have C++like macros. ;) – pepr May 17 '18 at 13:50