2012-05-10 7 views
5

は、私は前にこれをやったことがない、と私はそれが壊れることを具体的な理由を考えることはできませんが、私は次のように変数を使用することが有効であることを確認したいと思います:LINQ文内の呼び出しで変数を使用するのは安全ですか?

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; 
} 
+2

これは最悪の方法と考えられます。 – asawyer

+0

@asawyer、特に、あなたはそれを何と言うのですか? – sblom

+0

@asawyerとは対照的に、何ですか? –

答えて

10

これは動作します - 実際には完全にクエリを評価する必要があります。

しかし、この動作は非常に奇妙であり、私が強く避けることになります。 outパラメータはクエリ内で直接使用されているため、動作はここではかなり正常です(ただし、これ以外は何もしない限り)が、このユースケース固有のものであり、一般的な「ルール」ではなくLINQと混在しています。

問題は、LINQの遅延実行が出てパラメータが設定されるようにしますということですが、あなたがそれを宣言するときには、結果の列挙ではないを使う場合のみです。これにより、予期しない動作が発生し、ソフトウェアの保守や理解が困難になることがあります。

私は個人的にはちょうど別の方法を書き、としてクエリを書くことができるようにするためにそれを使用します。これは、はるかに明確で、かつミスやエラーを起こしにくい

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

。また、後でこれを変更しようとすると、並列化(つまり:.AsParallel()を介してPLINQ)と他の手法でも動作します。

+3

'b'が共有されているので、' Parallel'のような並行評価を引き起こす可能性があるものの周りには巨大な警告があります。 –

+0

@MarcGravell間違いなく、私はその理由のためにこれらの問題を持たない代替案を示すために編集しました。 –

3

これは意味的には合法ですが、安全かどうかは、どのように行うかによって大きく異なります。ここでの基本的な危険は、遅れている可能性のある、おそらく決して実行されていない式とローカルの割り当てを組み合わせることです。

コレクションが空の場合、GetNameへの呼び出しは、この場合実際には起こりません。したがって、常に元の値のfalseを維持することができます(これは、C#コンパイラがここでデフォルト値を宣言することを強制する理由です)。このセマンティックがあなたのプログラムでOKなら、bの使用は完全にうまくいきます。実際には、bはメソッドの呼び出し後にのみ使用されるため、このシナリオにあるようです。

しかし、これは私が一般的に回避するものです。コーナーケースでしか失敗しないような方法でこれを間違えることは非常に簡単です。

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

は、より複雑なシナリオについては、賢明という名前のプロパティを持つタイプでも良いだろう:より安全なアイデアは、タプルになります。

+0

こんにちは、私は答えをマークするために投票の過半数を行ったが、私は最初の場所でvarに頼りにする原因となった問題を回避するので、私はあなたのソリューションが大好きであることを伝えたいと思った。 –

+0

@とにかく2つのオプションが1より優れています:) –

関連する問題