5

議論が起こっています:

IListを持つクラスがあります。Factは抽象的な基本クラスであり、いくつかの具体的なサブクラス(PopulationFact、GdpFactなど)があります。

もともとは、この方法で、つまり次のように、特定のファクトを照会していました。

.Facts.FirstOrDefault(x => x.Year == 2011 && x is GdpFact)

しかし、今では、代わりにFactType列挙型を導入する必要があるかどうかという疑問が提起されています。

.Facts.FirstOrDefault(x => x.Year == 2011 && x.FactType == FactType.Gdp)

おそらくより速いので、提案が提起されました。パフォーマンスの違いを識別しようとするテストを作成していないことは認めますが、2つの質問があります。

1)このような「タイプのクエリ」は本質的に悪いですか?
2)ファクトが強く型付けされていることを考えると、とにかくFactType列挙型を追加するだけでは不要ではありませんか?

更新 明確にするために、これはオブジェクトとGdpFact:Factに対するLINQです。

UPDATE 2 現在の典型的なデータ(4つの事実)を使用して測定し、結果は次のとおりです。

列挙型でのルックアップ:0.29660000000000003ミリ秒タイプでのルックアップ:0.24530000000000002ミリ秒

したがって、このコンテキストではタイプルックアップが高速です。受け入れた答えを慎重に選びます。

4

5 に答える 5

3

パフォーマンスに関連するすべてのタイプの質問は、具体的なアプリケーションコンテキストに厳密に依存しているため、ここで提供される回答は、具体的なケースに対して部分的に正しい/間違っている可能性があります。

これを念頭に置いて:

列挙値のチェックは、タイプのチェックよりもかなり高速である必要があります。最初のケースでは、等しい2整数(列挙値)をチェックするだけです。

しかし、それはオブジェクトにもう1つのフィールドを導入します。これは、正しい値(単体テスト)を持つために追跡する必要があります。これは、CLR正しい型の初期化を考慮しているため、2番目のケースでは必要ありません。

関連する量のデータに対してアイデアをプロファイリングする方が良いと思います。通常、アプリは動作し、正しいアイデアが出てきます

于 2012-11-28T16:10:38.623 に答える
3

私はテストを行いました、1000000回の反復の私の結果はおよそです

ByCast 166ms
ByType 84ms
ByEnum 98ms

したがって、enum実際には不要で低速ですが、それほどではありません。これはそれほど驚くべきことではありません。型システムは.NetFrameworkの基本です。

以下に転記されたテストコード、正誤表についてお詫びします

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

class Program
{
    private enum TypeOfFact
    {
        Gdp,
        Other
    }

    private abstract class Fact
    {
        public virtual int Year { get; set; }
        public abstract TypeOfFact FactType { get; }
    }

    private class GdpFact : Fact
    {
        public override TypeOfFact FactType
        {
            get { return TypeOfFact.Gdp; }
        }
    }

    private class OtherFact : Fact
    {
        public override TypeOfFact FactType
        {
            get { return TypeOfFact.Other; }
        }
    }

    static void Main()
    {
        Ilist<Fact> facts = new List<Fact>
            {
                new GdpFact { Year = 2010 },
                new OtherFact { Year = 2010 },
                new GdpFact { Year = 2009 },
                new OtherFact { Year = 2009 },
                new GdpFact { Year = 2011 },
                new OtherFact { Year = 2011 },
            };

        const int interations = 1000000;

        var funcs = new List<Func<IList<Fact>, Fact>>
            {
                ByList,
                ByType,
                ByEnum
            };

        // Warmup
        foreach (var func in funcs)
        {
           Measure(5, func, facts);
        }

        // Results
        foreach (var result in funcs.Select(f => new
            {
                Description = f.Method.Name,
                Ms = Measure(iterations, f, facts)
            }))
        {
            Console.WriteLine(
                "{0} time = {1}ms",
                result.Description,
                result.Ms);
        }
    }

    private static long Measure(
        int iterations,
        Func<IList<Fact>, Fact> func,
        IList<Fact> facts)
    {
        var stopwatch = new Stopwatch();
        stopwatch.Start();
        for (var i = 0; i < iterations; i++)
        {
            func.Invoke(facts);
        }

        stopwatch.Stop();
        return stopwatch.ElapsedMilliseconds;
    }

    private static Fact ByType(IList<Fact> facts)
    {
        return facts.FirstOrDefault(f =>
            f.Year == 2011 && f is GdpFact);
    }

    private static Fact ByEnum(IList<Fact> facts)
    {
        return facts.FirstOrDefault(f =>
            f.Year == 2011 && f.FactType == TypeOfFact.Gdp);
    }

    private static Fact ByCast(IList<Fact> facts)
    {
        return facts.OfType<GdpFact>()
            .FirstOrDefault(f => f.Year == 2011);
    }
}

この質問は関連があるようです。

于 2012-11-28T17:34:13.467 に答える
2

これはおそらく問題を探している解決策ですか?

具体的なサブタイプと基本型に列挙型の両方があると、デザインがわかりにくくなる可能性があると思います。誰かが後でやって来て、新しい具象クラスを書いているのに、列挙型に追加する必要があることに気付いていないことを想像できます...

パフォーマンスに特定の問題がある場合を除いて、代わりに明快さを優先したいと思います。したがって、異なる具象クラスが必要な場合(そして、それが最初にコーディングした方法であるため、必要だと思います)、列挙型に移動するのではなく、型に固執します。

于 2012-11-28T16:19:24.500 に答える
2

あなたのオリジナルのアプローチは素晴らしいと思います。'is'キーワードは、この目的のために提供されています。MSDNは、「is」の使用を推奨していません。列挙型の使用は過剰に設計されているようです。コードをシンプルに保つようにしてください。ほとんどの場合、コードの行数が少ないほど良いです。

于 2012-11-28T16:27:00.650 に答える
2

列挙値のチェックは実行時の型チェックよりも高速になる可能性がありますが...

  1. これはマイクロ最適化です。実際のシナリオでは、パフォーマンスの違いに気付くことはほとんどありません。
  2. それは物事をより複雑にし、より複雑になると壊れやすくなります。
    たとえば、あなたや同僚の1人が誤ってこのようなことをしているのを防ぐにはどうすればよいでしょうか。

    public class PopulationFact : Fact
    {
        public FactType FactType = FactType.GdpFact;  // should be PopulationFact
    }
    

私はタイプチェックに固執します。実際には、それを実行する組み込みのLINQメソッドがあります。

.Facts.FirstOrDefault(x => x.Year == 2011).OfType<GdpFact>()
于 2012-11-28T16:41:33.857 に答える