Skip to content
This repository has been archived by the owner on Jul 18, 2023. It is now read-only.

boolean guard question #30

Closed
eiredrake opened this issue Jun 11, 2019 · 3 comments
Closed

boolean guard question #30

eiredrake opened this issue Jun 11, 2019 · 3 comments
Labels

Comments

@eiredrake
Copy link

eiredrake commented Jun 11, 2019

I am a big fan of doing checks on every argument that comes into a given function. I'd rather have something immediately throw an exception when given invalid data than find out ten functions in that my X value is invalid. Unfortunately if not then assert throw exception checks are very spammy. As a dyslexic, Dawn.Guard is awesome.

But I'm having some trouble validating a simple boolean.


var myStuff = true;
var myOtherStuff = true;

// this works
Guard.Argument(() => stuff).True();
// this works
Guard.Argument(() => myStuff).True();
// this throws the exception
Guard.Argument(() => myStuff || myOtherStuff).True();

throws an exception:
A member expression is expected.
Parameter name: e

at Dawn.Guard.Argument[T](Expression1 e, Boolean secure) in C:\Users\ekramer\Documents\Visual Studio 2017\Projects\guard\src\Guard.Argument.cs:line 61 at Dawn.Tests.BooleanTests.GuardSupportsBooleans(Nullable1 true, Nullable`1 false) in C:\Users\ekramer\Documents\Visual Studio 2017\Projects\guard\tests\BooleanTests.cs:line 13

I went so far as to clone the repository so I could examine the unit tests for booleans and the very next line of

Guard.Argument(() => @true).True();

works just fine. The only difference is that @true is a nullable boolean.

Could someone explain what I'm doing wrong here?

thanks,
E

@safakgur
Copy link
Owner

safakgur commented Jun 11, 2019

Hi, I'm glad you liked the library.

Guard.Argument overloads expect the value and name of a single argument, and failed preconditions throw exceptions for that specific argument. The reason why () => myStuff || myOtherStuff doesn't work is that it does not represent a single argument.

var a = 1;
var b = 2;

// The following lambda expression is a MemberExpression that represents the variable "a".
// Guard.Argument compiles that expression to get the value, 1.
Guard.Argument(() => a);

// The following lambda expression is a BinaryExpression that adds "a" and "b".
// It doesn't represent a single parameter which Guard can get the value and name of.
Guard.Argument(() => a + b);

If your method accept multiple arguments, you need to have a Guard.Argument(...) call for each. This allows the ParamName and Message properties of the exceptions thrown to be unique per argument.

If you're sure that you want to threat a combination of arguments as a single argument, you can use the Guard.Argument overload that accepts the argument value and name as separate parameters.

void Foo(bool a, bool b)
{
    // Here we specify a combination of values, and a custom name
    // to be included in exception messages.
    Guard.Argument(a || b, "a or b").True();
}

Update: However, I would recommend this approach instead:

void Foo(bool a, bool b)
{
    // Here, both a and b can be false if the other one is not.
    // Since "a" is the first argument, we'll blame "b" if it's not consistent with "a".
    if (!a)
        Guard.Argument(() => b).True("b can only false when a is true.");
}

I hope this helps, please let me know if you have any more questions.

@eiredrake
Copy link
Author

eiredrake commented Jun 12, 2019

ah so the 'e' in question that it's complaining about is the name of the argument? Yep that helps. The check was to make sure a particular JSON fragment had one of two options in it or not. A sanity check basically. I appreciate the help!

@safakgur
Copy link
Owner

Kind of.

e is the lambda expression you specify. It's body is a MemberExpression when you write it like () => foo. A member expression has a name we can use (foo). We can also compile the expression to get its value, so we can have both the name and value of the argument - it's just for convenience over the overload that accepts the name and value separately.

But when you write e like () => foo || bar, it's body becomes a BinaryExpression. Now it's the logical OR result of two different member expressions. We can still compile it and get a value but we now don't have a name, because the expression doesn't point to a single named argument.

Is the JSON fragment a Newtonsoft JObject that you accept as a parameter? Maybe you can use member guards to validate it. But I guess writing a "JSON to model" parser function and using Guard.Wrap with it would make more sense. Though you'd have to throw exceptions manually for invalid JSON values in that function.

I hope this clarifies things.

@safakgur safakgur closed this as completed Sep 4, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

2 participants