简化表达式选择器

时间:2014-11-06 14:37:12

标签: c# lambda

目前我在一个名为“Ensure”的类中使用此代码,它本质上是一个静态方法的快捷类,用于使抛出异常更容易,所以我不是经常写出至少3要做一个例外的行,它总是可以在一行完成。

    [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);
    }

我的问题是,目前在致电Ensure.ArgumentNotNull时,我必须这样做:

Ensure.ArgumentNotNull(arg, "arg");

Ensure.ArgumentNotNull(() => arg);

因为我需要这个名称能够解释哪个参数在异常中引起了异常。

有没有办法能够在不需要lambda的ArgumentNotNull部分的情况下调用() =>并简单地调用Ensure.ArgumentNotNull(arg)并且仍然能够获取该参数的名称通过,而不必专门传递名称。

4 个答案:

答案 0 :(得分:2)

  

有没有办法能够在不需要lambda的ArgumentNotNull部分的情况下调用() =>并简单地调用Ensure.ArgumentNotNull(arg)并且仍然能够获取该参数的名称通过

我对此表示怀疑,因为值没有元数据来确定它是传入的参数,变量还是文字。价值并不总是一个论点 - 没有什么能阻止你调用Ensure.ArgumentNotNull(null);

答案 1 :(得分:1)

这是有效的

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");
        }
    }

OP问题的替代答案

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

简化的lamdba将成功。

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();

        }

    }

答案 2 :(得分:0)

如果您在调用throw new ArgumentNullException("argName");时担心出错,那么您真的想使用Fody.NullGuard而不是当前的解决方案。

你像这样使用Fody.NullGuard

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
    }
}

编译完成后,Fody将重写IL,因此其功能与

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

    public void AnotherMethod(string arg)
    {
    }
}

答案 3 :(得分:0)

除了Aron提到的Fody.NullGuard(仅选择退出模型)和其他AOP框架(PostSharp)之外,我不知道你可以使用任何机制,除非有一定程度的代码混乱。

但是,您可以使用匿名类型,而不是使用表达式树,它们都会导致显着的运行时性能损失并且在语法方面受到限制。这种方法在性能方面相对较好,有几个假设:

  • API的用户将仅将其用于参数断言
  • C#中匿名类型实现的细节不会发生显着变化

API的用法如下所示:

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

实施太大而无法在此处显示,因此可以在this gist中找到。此外,它还可以扩展到范围验证等。