リスト< T>キャストアルゴリズムとパフォーマンスの考慮事項
-
03-07-2019 - |
質問
次のコードがあります
class Program
{
static void Main(string[] args)
{
List<A> aList = new List<A>();
var aObj = new A();
aObj.Go(aList.Cast<IB>());
}
}
class A : IB
{
public void Go(IEnumerable<IB> interfaceList)
{
foreach (IB ibby in interfaceList)
{
Console.WriteLine("Here");
}
}
}
interface IB
{
void Go(IEnumerable<IB> interfaceList);
}
}
最初はリストを渡そうとしましたが、機能しません。 SO から多くの支援を受けた後、IEnumerableを渡すことがオブジェクトを.ofType(IB)として取得する唯一の方法。
残念なことに、私のコードでは、次の行が数千回実行されます。
aList.Cast<IB>();
アルゴリズムで(ILで)実装される方法と、その時間順序が何かを知っている人がいるかどうか疑問に思っていました。
つまり、各アイテムをキャストするだけのforeachループよりも高速ですか、それとも正確に何をするのですか?
編集メインクラスは、実際のオブジェクトのリストを保持する必要があります。しかし、読者はインターフェースを介してのみそれらに触れることができます。
解決
Go
を次のように変更する必要があります:
public void Go<T>(IEnumerable<T> interfaceList)
where T : IB
{
foreach (IB ibby in interfaceList)
{
Console.WriteLine("Here");
}
}
その後、 Cast
を呼び出す必要はありません。 Castのソースコードの実装は非常に単純であると思われますが、3.5と3.5SP1の間で変更されたと思います。ただし、通常のイテレータブロック形式で新しいステートマシンなどを設定する必要があるでしょう。可能であればそれを避ける方が良い。
新しいメソッドはジェネリックですが、通常は型推論が自動的に処理するため、 T
を明示的に指定する必要はありません。
他のヒント
次のようなリストを宣言するだけではありません:
List<IB> aList = new List<IB>();
具体的なクラスのリストが必要な特別なものはありますか?
その場合、リストをドメインの一部にします。 (たとえば)IIBCollectionのようなインターフェイスを使用して、リーダーがアクセスできるようにするメソッドを公開します。例:
interface IIBCollection{
IEnumerable<IB> IBs { get; }
}
// and in your implementation you can do
IEnumerable<IB> IBs {
get {
foreach(IB ib in innerList) yield return ib;
}}
これは、各アイテムをキャストするforeachよりも少し遅いCastIteratorとして内部的に実装されています。
Castメソッドはリストをループして各アイテムをキャストします。
リストを何千回も使用する場合は、キャストの結果をリストとして保存してください。
それが不可能な場合(つまり、毎回リストを変更する場合)、 List&lt; A&gt;
ではなく、 List&lt; IB&gt;
を使用することを検討してください。
これは、C#の共分散の目的ではありませんか?あなたが何をしようとしているのかわかりませんので、何千回も実行された理由についてコメントすることはできません。
2つのメソッド(キャスト拡張メソッドとキャスト付きforループ)のベンチマークを行うだけで、かなり簡単になります。しかし、CastはEnumerableクラスの拡張メソッドであり、IEnumerablesを一般的に扱うことを考えると、これがまさに実装であると思います。最高の速度が必要な場合は、Listで特に機能する独自の拡張メソッドを実装することをお勧めします(インデックスで各要素を取得します)。それでも、どちらの方法もO(n)時間かかるはずなので、違いはそれほど大きくないはずです。それにもかかわらず、ベンチマークに値するものです...