0

I would like to check by attribute that value passed to method is correct, because adding if condition to method body make code less readable (in my opinion)

For example,

    public object GetSomething(int id)
    {
        // there could be more conditions
        if (id<= 0)
            throw new ArgumentOutOfRangeException(nameof(id), $"Forbiden value: '{id}'");
        // real logic
        ...

I want to check it is such way

    [PossitiveValueAttr(id)]
    public object GetSomething(int id)
    {
        // real logic

or (I would prefer that)

    public object GetSomething([PossitiveValueAttr] int id)
    {
        // real logic

1) Do you know any similar existing solution?

2) How can I pass method's argument to my attribute?

Cœur
  • 37,241
  • 25
  • 195
  • 267
Jacek
  • 11,661
  • 23
  • 69
  • 123

2 Answers2

1

Inspired by Manfred...

So if for any method defined as

void DoSomething([Attr1] Type1 param1, [Attr2] Type2 param2, ...)
{
}

You're happy to add as the first statement of the method:

Validate(() => DoSomething(param1, param2, ...)) 

Where Attr1/Attr2 etc. all implement ValidationAttribute:

[AttributeUsage(AttributeTargets.Parameter)]
public class ArgumentAttribute : Attribute
{
    public void Validate(string argumentName, object value)
    {
        // throw exception if not valid
    }
}

Then you can implement Validate as:

static void Validate(Expression<Action> expr)
{
    var body = (MethodCallExpression)expr.Body;

    foreach (MemberExpression a in body.Arguments)
    {
        FieldInfo fi = a.Member as FieldInfo;
        ParameterInfo pi = body.Method.GetParameters().FirstOrDefault(p => p.Name == fi.Name);
        ValidationAttribute va = pi?.GetCustomAttribute<ValidationAttribute>();
        if (va != null)
            va.Validate(pi.Name, fi.GetValue(((ConstantExpression)a.Expression).Value));
    }
}

And use it generically wherever you need to perform argument validation defined via custom attributes.

The main drawback is that ValidationAttribute can't be generic, so you always get a plain 'object' passed in that you'll have to downcast.

Dylan Nicholson
  • 1,301
  • 9
  • 23
0

Attributes can't magically cause any behavioral change in your application unless you have code that looks for and acts on them. Further, attributes can only be determined if you have reflection information, which you can't get for function arguments. But for your particular example (must be positive), why not just used an unsigned int?

Dylan Nicholson
  • 1,301
  • 9
  • 23
  • I agree with the general argument you make, but *"Further, attributes can only be determined if you have reflection information, which you can't get for function arguments."* is just completely wrong: https://dotnetfiddle.net/Widget/LgE3eI – Manfred Radlwimmer Nov 17 '17 at 07:49
  • I didn't say you couldn't place attributes on function arguments, but I don't believe you can have code that can at run-time "know" which function argument it's dealing with and extract the value to perform the relevant operation. Although apparently this is possible in Unity, which supports method interceptors. – Dylan Nicholson Nov 17 '17 at 07:55
  • I think you misunderstand, I totally agree with the *"Attributes can't magically cause any behavioral change in your application"* part, I was referring to *"which you can't get for function arguments"*. – Manfred Radlwimmer Nov 17 '17 at 07:56
  • Ok, so how would you extend your example to have the attributes for each parameter validate the *value* of each parameter that's passed in? And the very fact you need to call GetMethod("Test") from a method that we already know is called "Test" is hardly ideal for a generic parameter validation framework. – Dylan Nicholson Nov 17 '17 at 08:00
  • That was a minimal example to show that you **can** get Attributes from function arguments. It is in way to be meant as a template or even attempted solution. As I said before, I completely agree with the general argument you make - but that part is technically incorrect. – Manfred Radlwimmer Nov 17 '17 at 08:05
  • It was poorly phrased, granted, what I meant is that you can't at runtime enumerate the parameters passed to the current method and find their attributes then use them to perform operations on their current values (which is something you CAN do with fields/properties). – Dylan Nicholson Nov 17 '17 at 08:08