动态检查 IsNull 值的嵌套值
使用扩展方法动态检查表达式树中的值为 Null。
引言
这基于我在 Stack Overflow 上找到的一个方法(经过一整天的搜索)。这个方法是我一直很需要的,尤其是在使用 DevExpress 时。因此,如果您在树形表达式中有许多方法/类,而不是在获取值之前检查每个单独的值是否为 Null
,我找到了一种方法,可以检查是否有任何值为 null
,如果没有,则返回您正在查找的值。
背景
正如我所说,这在 Devexpress 中非常有帮助,而且在 C# 和 MVC 中也非常有用。
Using the Code
假设您有一个名为 Contracts
的类。在该 contracts
类中,您拥有与该类关联的所有预订(Booking
类)。在 Bookings
类中,您拥有人员详细信息(Person
类)。其中,您拥有人员的安全许可及其登录系统的用户名(Security
类)。所以,假设我想获取与合同关联的人员的用户名。
通常,您需要获取第一个值,然后是下一个,再下一个,依此类推,如下所示...
If (Contracts != null && Contracts.Booking != null &&
Contracts.Booking.Person != null && Contracts.Booking.Person.Security != null)
{
string personUsername = Contracts.Booking.Person.Security.Username;
}
这可能会变得很麻烦,尤其是在您拥有大量信息,这些信息都来自不同类别的不同值时。如果代码或类发生更改,也会花费很多精力。
现在我找到的解决方法是创建一个在项目中全局可访问的扩展类。所以我选择了一个公共函数作为存放的地方。将要使用的方法是 C# 的内置函数,称为 ExpressionVisitor
。我将把我的类命名为 IsNullVisitor
。
为此,只需要以下命名空间。
using System;
using System.Linq.Expressions;
using System.Reflection;
在这个例子中,我只需要 2 个类。一个扩展 ExpressionVisitor
,然后一个我称之为 helper
的 static
类。在我的 helper
类中,我使用 4 个 static
对象进行测试。
那么,让我们从 ExpressionVisitor
类开始。我的看起来如下:
public class IsNullVisitor : ExpressionVisitor
{
public bool IsNull { get; private set; }
public object CurrentObject { get; set; }
protected override Expression VisitMember(MemberExpression node)
{
base.VisitMember(node);
if (CheckNull())
return node;
var member = (PropertyInfo)node.Member;
CurrentObject = member.GetValue(CurrentObject, null);
CheckNull();
return node;
}
private bool CheckNull()
{
if (CurrentObject == null)
IsNull = true;
return IsNull;
}
}
通过这种方式,我们将使用 Linq 表达式中构建的辅助函数来测试每个类的“级别”,直到找到一个值。但是,如果任何一个对象返回 Null
,它将返回 null
值。
现在,为了做到这一点,我将向您展示我使用的第一个对象。这个对象接受任何对象并测试它是否包含任何 null
对象,如果没有,它将返回对象的值。我将使用 linq 表达式来检查每个单独的对象,如果任何一个对象为 null
,则返回 null
值,如下所示。请注意,所有对象都将包含在我创建的 Helper
类中。
public static class Helper
{
// Static objects here...
}
在这个类中,我创建了所需的 isNull
检查。第一个是测试对象并按原样返回对象或 null
值。
public static object IsNullValueReturn<T>
(this T root, Expression<Func<T, object>> getter)
{
var visitor = new IsNullVisitor();
visitor.CurrentObject = root;
visitor.Visit(getter);
If (visitor.IsNull)
return null;
return visitor.CurrentObject;
}
那么,让我们以上面的例子为例,看看我们将如何查询它...
string personUsername = Contracts.IsNullStringReturn(c => c.Booking.Person.Security.Username);
这现在将进行测试,直到找到 null
。因此,它将测试 Contracts
,如果不是 null
,它将测试 Contracts.Booking
,如果不是 null
,它将继续测试,直到找到 null
。它将继续遍历 MemberExpression
,直到其中一个返回 true
。因此,它将获取 base.Visit
成员(在本例中为 Contracts
)并继续测试,直到找到 Null
。因此,它测试 base.VisitMember(MemberExpression)
是否为 null
并返回。如果为 false
,它将获取 MemberExpression
成员的 PropertyInfo
并测试是否可以获取 Value
,如果不能,则为 null
。然后它第二次测试 Null
值,如果不为 null
,它将继续(通过“tree
”)直到找到 null
值导致其返回,或者如果没有找到 null
,在这种情况下可以返回最后测试对象的值。因此,在示例中,如果没有测试到 null
,它将返回 Username
值。
我将要进行的第二次测试是,我特别希望在测试的对象不是 string
值时返回一个 string
值(ToString()
方法)。由于 string
可以是 Null
,如果找到了 null
值,我仍然可以返回该值作为 Null
。
public static string IsNullStringReturn<T>(this T root, Expression<Func<T, object>> getter)
{
var visitor = new IsNullVisitor();
visitor.CurrentObject = root;
visitor.Visit(getter);
if (visitor.IsNull)
return null;
return visitor.CurrentObject.ToString();
}
第三个要展示的是仅返回第一个字母,或者如果有任何 null
值,则返回字符串 'N/A
'。
public static string IsNullNACharReturn<T>(this T root, Expression<Func<T, object>> getter)
{
var visitor = new IsNullVisitor();
visitor.CurrentObject = root;
visitor.Visit(getter);
if (visitor.IsNull)
return "N/A";
return Format.FirstChar(visitor.CurrentObject.ToString());
}
我的 firstChar
方法中使用的代码如下:
public static string FirstChar(string item)
{
if (!String.IsNullOrEmpty(item))
if (item.Trim().Length > 0)
return item.Trim().Substring(0,1);
return string.Empty;
}
这可能会返回一个空值,但您仍然可以测试它是否为 null
或 string.Empty
来进行准确的计算等。
最后一个将是测试并返回一个 bool
值。
public static bool? IsNullBoolReturn<T>(this T root, Expression<Func<T, object>> getter)
{
var visitor = new IsNullVisitor();
visitor.CurrentObject = root;
visitor.Visit(getter);
if (visitor.IsNull)
return null;
return Convert.ToBoolean(visitor.CurrentObject);
}
我将此方法制作为可为空的 bool
,以便您可以获取 null
值或对象的 bool
值。这样,就可以检查它是否为 null
,如果不是,则返回对象的布尔值。
如果您需要返回一个值而无需无休止的测试,所有这些都非常有帮助。我的做法的优点是您可以直接使用该值,即使它有一个可为空的对象,您仍然会有一个值。因此,您可以在不先测试可为空的对象的情况下使用该值。
有无数的对象供您使用,然后只需自定义返回值等。无论您像我一样使用不同的类型并因此使用不同的名称,还是只是重载/覆盖同名对象,这取决于您。
关注点
请注意,不幸的是,在第一个版本中,这并不完全是万无一失的(并且了解代码);-)。所以请记住,如果您使用以下方法,很可能会崩溃并导致错误:
string personUsername = Contracts.IsNullBoolReturn(c => c.Booking.Person.Security.Username);
因此,在使用此功能时,请确保使用正确的对象。好吧,如果您不确定,总可以使用 object。:-P
var personUsername = Contracts.IsNullValueReturn(c => c.Booking.Person.Security.Username);
最后,请注意,它的缺点是,当您将任何 null
检查器用于强类型值时,您不能使用返回值。尽管返回值是为了例如一个 string
值,但根据代码,null visitor 不会返回 string
,而是返回一个未知的变量。所以这个问题是,当使用 bool 和返回 'N/A' 的值时,它确实会不足。但我使用 90% 的 isNullValueReturn
,很少使用 isNullStringReturn
,并且仅在特定报告中使用 isNullBoolReturn
和 isNullNAReturn
。
历史
- 版本 1