0

I have the method below and I want to check if any of the parameters are null. If any are null, I want to be able to print an error that says something like: "'name' is missing" with the correct variable displayed.

public void Method(string name, DateTime? date)
{  }

I need to be able to use my own custom error handling, so nothing built in like ArgumentNullException. I know an if statement can be used, but I was hoping to use some sort of validator. Is there a way to do this so that it can be used across multiple methods and returns the correct variable name in the message?

Brad Germain
  • 597
  • 1
  • 9
  • 22
  • Why do you say `ArgumentNullException` will prevent you from using your own custom error handling? – Dour High Arch Apr 07 '14 at 18:26
  • 1
    This question is slightly different, but the accepted answer might contain just what you're looking for: http://stackoverflow.com/questions/1503651/is-it-possible-to-iterate-through-methods-parameters-in-c – GolezTrol Apr 07 '14 at 18:29
  • I have my own for handling that. I also was hoping to avoid answers related to built in exception handling like `name.ThrowIfNull("Error")`. – Brad Germain Apr 07 '14 at 18:30

6 Answers6

1

Aspect oriented programming is the usual way to approach such requirements in a generic way. You may want to have a look at PostSharp, Castle DynamicProxy or Microsoft's Unity and there are a couple of other alternatives. But this is a huge hammer, think carefully before using it.

For your specific problem compile time weaving would be sufficient and require no code changes or at most adding some attributes but it requires changes to your build process. Run time weaving will not affect your build process but will probably require a non-trivial amount of code changes.

PostSharp Ultimate includes the aspect you want, at least almost, or you can buy it separately and probably use with the free edition. But it is not hard to implement it on your own.

Daniel Brückner
  • 59,031
  • 16
  • 99
  • 143
  • Thanks for the post. I obviously have to install something locally for these. Since I'm working in a group, they would need to install it as well, correct? Also, would something have to be installed on our dev server to run it? – Brad Germain Apr 07 '14 at 18:42
  • There are two ways. Compile time weaving will inject new code at compile time and therefore requires a new build step. Run time weaving will inject the code at run time usually by creating proxy objects. In this case the build process is not affected but you have to replace your `new Foo()` statements with a call to a proxy factory. – Daniel Brückner Apr 07 '14 at 18:47
  • Ok, so will this affect all projects and classes or only the ones where I use PostSharp? – Brad Germain Apr 07 '14 at 19:01
  • If you want to go with PostSharp, PostSharp will run as a new build step and you can enable or disable it per project. And it will of course only modify the methods you want. You can for example mark methods with an attribute or you can specify regular expressions to select types and methods to modify. It really is a *very* powerful tool and can solve your problem with ease but there is definitely a somewhat steep learning curve. – Daniel Brückner Apr 07 '14 at 19:08
1

I don't know if this is what you looked for, but did you hear about design by contract?

Check the official code contracts' article on MSDN.

One of possible use cases of code contracts is parameter validation.

For example, your code would look like this:

public void Method(string name, DateTime? date)
{  
     Contract.Requires(!string.isNullOrEmpty(name), "'name' cannot be null");
     Contract.Requires(date.HasValue, "'date' is required");
}

You can also listen for contract exceptions using a global contract handler.

If you look further at code contracts, you'll find that you can create contracts for interfaces and abstract class so any implementation will get their contracts injected in their body.

Matías Fidemraizer
  • 63,804
  • 18
  • 124
  • 206
  • I'm not too familiar with code contracts, but I'm primarily looking for a more modular approach so it can be used across multiple methods. Similar to a validator. – Brad Germain Apr 07 '14 at 19:23
1

From what i understand, you are looking for something like this.

Console Output:

Method: test, Parameter: a has a null value
False
Method: test, Parameter: b has a null value
False
True

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace ConsoleApplication3
{
    class Program
    {
        static void Main(string[] args)
        {

            Console.WriteLine(test("", "Something").ToString());
            Console.WriteLine(test("something", null, "blah").ToString());
            Console.WriteLine(test("something", "blah", "blah").ToString());
            Console.ReadLine();
        }

        public static bool test(string a, string b)
        {
            if (Validator.isNullOrEmpty(MethodBase.GetCurrentMethod(), a, b))
                return false;
            return true;
        }
        public static bool test(string a, string b, string c)
        {
            if (Validator.isNullOrEmpty(MethodBase.GetCurrentMethod(), a, b, c))
                return false;
            return true;
        }
    }

    class Validator
    {
        public static bool isNullOrEmpty(MethodBase method, params object[] values)
        {
            ParameterInfo[] pars = method.GetParameters();
            bool ret = false;
            for(var i = 0; i < pars.Length; i++)
            {
                ParameterInfo p = pars[i];
                string type = p.ParameterType.ToString().ToLower();
                try
                {
                    switch (type)
                    {
                        case "system.string":
                            ret = String.IsNullOrEmpty((string)values[i]);
                            break;
                        case "system.int32":
                        case "system.double":
                        default:
                            ret = values[i] != null;
                            break;
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine(String.Format("Method: {0}, Parameter: {1} has an incorrect value for a {2} with '{3}'",
                        method.Name, p.Name, type, values[i]));
                    Console.WriteLine(ex.Message);
                    return true;
                }
                if (ret)
                {
                    Console.WriteLine(String.Format("Method: {0}, Parameter: {1} has a null value", method.Name, p.Name));
                    return ret;
                }
            }

            return ret;
        }
    }
}
  • I had an issue running your code, but that looks pretty close to what I was looking for. With a little debugging it looks like it would work fine. I found a different approach that looked a little _prettier_, but it is similar. I have it posted as one of the answers. Thanks for your post! – Brad Germain Apr 07 '14 at 20:08
  • It compiles and runs the one test case, but there still might be a couple issues with it cause its not extensively tested. Let me know if you need anything else! – Andrew J Mueller Apr 07 '14 at 20:22
0

For the string:

if(string.IsNullOrEmpty(name))

For the nullable:

if(date.HasValue)
Mike Cheel
  • 12,626
  • 10
  • 72
  • 101
0
public void Method(string name, DateTime? date)
{
    if( name == null ){ } // you may want: if( string.IsNullOrEmpty( name ) ){}
    if( date.HasValue ){ }
}
clcto
  • 9,530
  • 20
  • 42
  • Thank you for the response. I know that is one option, but I'm looking to use some sort of validator since I want it to be used across multiple methods. – Brad Germain Apr 07 '14 at 18:26
0

This is the solution I went with to avoid installing any new products and learning new technologies. @Daniel had some great suggestions that probably would work the best for this issue, but following the steps on this post I was able to get it to work in a similar way to what I was hoping for.

http://weblogs.asp.net/fredriknormen/archive/2008/05/08/how-to-validate-a-method-s-arguments.aspx

In addition, here is another interesting solution, although it requires an interceptor from Microsoft's Unity:

http://www.codinginstinct.com/2008/05/argument-validation-using-attributes.html

Brad Germain
  • 597
  • 1
  • 9
  • 22