在LINQ语句中的调用中使用变量是否安全?

时间:2012-05-10 21:45:32

标签: c# linq

我之前从未这样做过,虽然我想不出它会破坏的具体原因,但我想验证使用out变量是否有效如下:

void Main()
{
    var types = new [] { typeof(A), typeof(B) };
    bool b = false;
    var q = from type in types
            from property in type.GetProperties()
            let propertyName = GetName(property, out b)
            select new {
                TypeName = type.Name,
                PropertyName = propertyName,
                PropertyType = property.PropertyType.Name,
                IsNullable = b
            };
    q.Dump();
}

private string GetName(PropertyInfo property, out bool isNullable)
{
    string typeName;
    isNullable = false;
    var type = property.PropertyType;
    if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
    {
        isNullable = true;
        typeName = type.GetGenericArguments().First().Name;
    }
    else
    {
        typeName = property.Name;
    }
    return typeName;
}

3 个答案:

答案 0 :(得分:10)

这将有效 - 只要您实际完全评估查询。

但是,这种行为会很奇怪,而且我会强烈避免这种行为。由于out参数直接在查询中使用,因此这里的行为相当正常(前提是你没有对此做任何其他事情),但这是特定于这个用例,而不是使用的一般“规则”与LINQ混合。

问题是LINQ的延迟执行将导致out参数被设置,但仅当你使用得到的可枚举时,而不是在你声明它时。这可能会导致非常意外的行为,并导致难以维护和理解软件。

我个人只是编写一个单独的方法,并使用它来允许您的查询写为:

var q = from type in types 
        from property in type.GetProperties() 
        let propertyName = GetName(property)
        let nullable = GetIsNullable(property)
        // ...

这更加清晰,不容易出错和错误。它也适用于并行化(即:PLINQ via .AsParallel())和其他技术,如果有人试图稍后改变它。

答案 1 :(得分:3)

这样做在语义上是合法的,但它是否安全非常依赖于你如何做到这一点。这里的根本危险在于,您正在使用延迟(可能永远不会执行)的表达式来组合本地的赋值。

如果集合为空,则在这种情况下实际上永远不会发生对GetName的调用。因此它可能始终保持它的原始值false(这也正是C#编译器强制您在此处声明默认值的原因)。如果这个语义对你的程序没问题,那么b的完全使用是完全没问题的。实际上似乎是在这种情况下,因为b仅在调用方法后使用。

然而,这是我一般会避免的。很容易弄错,只会在极端情况下失败。

答案 2 :(得分:3)

这会起作用,只是,但是出于每一个错误的原因(并且这是一个不好的习惯,因为它在更一般的情况下是不安全的)。一个更安全的想法将是一个元组:

        let info = GetInfo(property)
        select new {
            TypeName = type.Name,
            PropertyName = info.Item1,
            PropertyType = property.PropertyType.Name,
            IsNullable = info.Item2
        };

....

private Tuple<string,bool> GetInfo(PropertyInfo property)
{
    string typeName;
    bool isNullable = false;
    ...
    return Tuple.Create(typeName, isNullable);
}

对于更复杂的场景,具有合理命名属性的类型会更好。