C# 在LINQ语句内的调用中使用out变量安全吗?

C# 在LINQ语句内的调用中使用out变量安全吗?,c#,linq,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 = GetN

我以前从未这样做过,虽然我想不出它会崩溃的具体原因,但我想验证一下使用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;
}

如果您确实对查询进行了完整的评估,这将起作用

然而,这种行为会非常奇怪,我会极力避免。由于out参数直接在查询中使用,因此如果您不做任何其他操作,那么这里的行为将相当正常,但这是特定于此用例的,而不是将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)和其他技术一起工作

从语义上讲,这样做是合法的,但它是否安全取决于你如何做。这里最基本的危险是,您正在将局部变量的赋值与延迟(可能永远不会执行)的表达式相结合

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

然而,这是我通常会避免的事情。这很容易出错,只会在极端情况下失败。

这会起作用,但由于各种错误的原因,这是一种坏习惯,因为在更一般的情况下,这是不安全的。更安全的方法是使用元组:

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

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

这将被视为最糟糕的做法。@asawyer,你为什么这么说?@asawyer,与什么相反?我正在键入一个响应,但我看到里德很好地覆盖了它。对于任何可能导致并发计算的问题,例如并行计算,都有一个巨大的警告,因为b是共享的。@MarcGravel肯定-我编辑了一个替代方案,显示了一个不会因为这个原因出现这些问题的方案。嗨,我同意投票多数的意见,但我想告诉你,我非常喜欢你的解决方案,因为它避开了导致我在第一个阶段求助于out var的问题place@Aaron没问题;2个选项总比1个好: