简化表达式选择器
本文关键字:选择器 表达式 | 更新日期: 2023-09-27 18:29:29
目前我正在一个名为"确保"的类中使用这段代码,它本质上是一个静态方法的快捷类,我使用它来简化抛出异常的过程,所以我不必经常写至少3行来执行异常,它总是可以在1行上完成。
[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)
,仍然可以获得传递的参数的名称,而不必专门传递名称。
有没有一种方法可以在不需要lambda的
() =>
部分的情况下调用ArgumentNotNull
,只需调用Ensure.ArgumentNotNull(arg)
,仍然可以获得传递给的参数的名称
我对此表示怀疑,因为值没有元数据来确定它是传入的参数、变量还是文字。该值并不总是一个参数——没有什么可以阻止您调用Ensure.ArgumentNotNull(null);
。
这适用于
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();
}
}
除了Aron提到的Fody.NullGuard(仅限选择退出模型)和其他AOP框架(PostSharp)之外,我不知道有任何机制可以在没有一定程度代码混乱的情况下用于此。
但是,您可以使用匿名类型,而不是使用表达式树(这会导致运行时性能的显著损失,并且在语法方面受到限制)。这种方法在性能方面相对较好,有几个假设:
- API的用户将仅将其用于参数断言
- C#中匿名类型实现的细节不会发生显著变化
API的使用看起来像:
void SomeMethod(object arg1, string arg2, List<int> arg3)
{
new { arg1, arg2, arg3 }.ShouldNotBeNull();
....
实现太大了,无法在这里显示,所以可以在这个要点中找到它。此外,它还可以扩展到范围验证等。
如果您担心在调用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)
{
}
}