4

At the moment i am using this code in a class i call "Ensure", it is essentially a shortcut class of static methods i use to make throwing exceptions easier, so i am not constantly having to write out a minimum of 3 lines to do an exception, it can all always be done on 1 line.

    [DebuggerHidden, DebuggerStepThrough]
    public static void ArgumentNotNull(object argument, string name)
    {
        if (argument == null)
        {
            throw new ArgumentNullException(name, "Cannot be null");
        }
    }

    [DebuggerHidden, DebuggerStepThrough]
    public static void ArgumentNotNull<T>(Expression<Func<T>> expr)
    {
        var e = (MemberExpression)expr.Body;

        var val = GetValue<T>(e);

        ArgumentNotNull(val, e.Member.Name);
    }

My issue is, currently when calling Ensure.ArgumentNotNull, i either have to do:

Ensure.ArgumentNotNull(arg, "arg");

or

Ensure.ArgumentNotNull(() => arg);

As i need the name to be able to explain which argument caused the exception in the exception its self.

Is there a way of being able to call ArgumentNotNull without needing the () => part of the lambda and simply call Ensure.ArgumentNotNull(arg) and still be able to get the name of the argument that was passed, without having to specifically pass the name as well.

bizzehdee
  • 20,289
  • 11
  • 46
  • 76
  • 2
    You can always wait for C# 6.0; you'll have [`nameof`](http://msdn.microsoft.com/en-us/magazine/dn802602.aspx) expressions. – InBetween Nov 06 '14 at 14:46
  • 2
    @DStanley: its not that its a burden, it just doesnt look as clean as it could, and it was a speculative question anyway, i asked because i didnt know the answer. – bizzehdee Nov 06 '14 at 14:55
  • Have you thought about using `Fody` for AOP to add Ensure at build type. Alternatively you could consider using Code Contracts and the Static Code Analysis to "prove" that the arg is never null. – Aron Nov 06 '14 at 16:34
  • @Aron: no, i havnt, because that isnt what i am trying to achieve. – bizzehdee Nov 07 '14 at 15:27
  • @bizzehdee You should take a look at this... https://github.com/Fody/NullGuard – Aron Nov 07 '14 at 18:54

4 Answers4

2

Is there a way of being able to call ArgumentNotNull without needing the () => part of the lambda and simply call Ensure.ArgumentNotNull(arg) and still be able to get the name of the argument that was passed

I doubt it, because values have no meta-data to determine if it was an argument passed in, a variable, or a literal. The value will not always be an argument - there's nothing preventing you from calling Ensure.ArgumentNotNull(null);.

D Stanley
  • 149,601
  • 11
  • 178
  • 240
  • That will change in C# 6.0 with `nameof` expressions. I think the needed infrastructure has been there for a while but the language never leveraged it. – InBetween Nov 06 '14 at 14:48
  • 3
    @InBetween Will it? You would still have to pass in the second parameter - `Ensure.ArgumentNotNull(arg, nameof(arg));` `nameof` has no magic to tell the function that the value passed in was named `arg` _in the calling function_. – D Stanley Nov 06 '14 at 14:52
  • @ChrisMcKelt The OP's question contains the code to do that. – Servy Nov 06 '14 at 15:20
  • @DStanley Very true, I stand corrected, you'd still need two arguments. – InBetween Nov 06 '14 at 15:45
  • But the value here is, if the variable is renamed, and the code compiles (which allows us to presume that the variable was renamed in all locations), then the second parameter will always be correct, instead of perhaps staying the old and incorrect parameter name. – ErikE Nov 06 '14 at 22:13
0

this works

public static void ArgumentNotNull(object argument)
    {
        StackFrame stackFrame = new StackTrace(true).GetFrame(1);
        string fileName = stackFrame.GetFileName();
        int lineNumber = stackFrame.GetFileLineNumber();
        var file = new System.IO.StreamReader(fileName);
        for (int i = 0; i < lineNumber - 1; i++)
            file.ReadLine();
        string varName = file.ReadLine().Split(new char[] { '(', ')' })[1];


        if (argument == null)
        {
            throw new ArgumentNullException(varName, "Cannot be null");
        }
    }

alternate answer to OP question

  'and still be able to get the name of the argument that was passed, without having to specifically pass the name as well.'

Simplified lamdba will do the trick.

myObject.ArgumentNotNull(x=>x.SomeProperty);  -- prop checking
myObject.ArgumentNotNull(x=>x);  -- objchecking

[DebuggerHidden, DebuggerStepThrough]
    public static void ArgumentNotNull<T>(this T obj, Expression<Func<T, object>> expr = null)
    {
        if (obj == null) throw new NullReferenceException();

        var body = expr.Body as MemberExpression;

        if (body == null)
        {
            var ubody = (UnaryExpression)expr.Body;
            body = ubody.Operand as MemberExpression;
        }

        if (body != null)
        {
            var property = body.Member as PropertyInfo;
            if (property == null) throw;
            if (obj.GetType().GetProperty(property.Name).GetValue(obj, null) == null) throw new NullReferenceException();

        }
        else
        {
            var ubody = (UnaryExpression)expr.Body;
            var property = ubody.Operand as MemberExpression;
            if (property != null)
                props[property.Member.Name] = obj.GetType()
                    .GetProperty(property.Member.Name)
                    .GetValue(obj, null);
            if (obj.GetType().GetProperty(property.Member.Name).GetValue(obj, null) == null) throw new NullReferenceException();

        }

    }
Chris McKelt
  • 1,378
  • 2
  • 17
  • 38
  • good write up on how to get property name/value via lambda expressions here - http://joelabrahamsson.com/getting-property-and-method-names-using-static-reflection-in-c/ – Chris McKelt Nov 06 '14 at 15:14
  • 1
    OP was asking how to get the argument name _without_ passing in a lambda. – D Stanley Nov 06 '14 at 15:16
  • 1
    Yes, but the OP already has a method to do it with a lambda (albeit more rudimentary than yours) but wants to be able to just pass the value. – D Stanley Nov 06 '14 at 15:39
  • @DStanley updated answer with a solution that works (no lambdas) - inspired in part from here - http://stackoverflow.com/questions/1718037/abuse-of-c-sharp-lambda-expressions-or-syntax-brilliance?rq=1 – Chris McKelt Nov 06 '14 at 22:51
  • Stack approach, if I remember correctly, will not necessarily work with release builds. – galenus Nov 12 '14 at 03:59
  • Believe you can change this slightly to [CallerMemberName],[CallerFilePath], [CallerLineNumber] attributes and not call the stack if you want - http://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.callermembernameattribute(v=vs.110).aspx – Chris McKelt Nov 12 '14 at 06:22
  • For this you should have the source code accessible in the production environment. – galenus Nov 12 '14 at 06:35
0

I am not aware of any mechanism you could use for this without some degree of code clutter, except for the Fody.NullGuard mentioned by Aron (opt-out model only) and other AOP frameworks (PostSharp).

However, instead of using expression trees, which both result in significant runtime performance penalty and are limited in terms of syntax, you can use anonymous types. This approach is relatively good in terms of performance, with a couple of assumptions:

  • that the user of the API will use it for arguments assertion only
  • that details of anonymous types implementation in C# will not change significantly

The usage of the API will look something like:

void SomeMethod(object arg1, string arg2, List<int> arg3)
{
    new { arg1, arg2, arg3 }.ShouldNotBeNull();
    ....

Implementation is too large to show here, so it can be found in this gist. Additionally, it can be extended for range validations, etc.

galenus
  • 2,087
  • 16
  • 24
-1

You really want to use Fody.NullGuard instead of your current solution if you are worried about making a mistake when calling throw new ArgumentNullException("argName");.

You use Fody.NullGuard like this

public class Sample
{
    public void SomeMethod(string arg)
    {
        // throws ArgumentNullException if arg is null.
    }

    public void AnotherMethod([AllowNull] string arg)
    {
        // arg may be null here
    }
}

After compilation, Fody will rewrite the IL so its functionally the same as

public class Sample
{
    public void SomeMethod(string arg)
    {
        if(arg == null) 
            throws new  ArgumentNullException("arg");
    }

    public void AnotherMethod(string arg)
    {
    }
}
Aron
  • 15,464
  • 3
  • 31
  • 64