"EnsureXXX"という種類のメソッドを持つクラスライブラリを設計しています。このメソッドの考え方は、呼び出しコードが引数に固有の初期化を必要とする以上のものを必要とするときはいつでも呼び出されます。それはASP.NetのEnsureChildControls
の方法と似ていますが、弁別子としての引数があります。メソッドのロジックが引数の組み合わせごとに1回だけ実行されるようにする方法はありますか?
例:このようなパターンはいくつかの場所で再利用し、非常に頻繁に呼ばれるように
public static class SomeUtilityClass {
public static void EnsureSomething(string arg1, int arg2, object arg3)
{
// Logic should be called once for each args combination
}
}
public class CallerClass
{
public void Foo()
{
SomeUtilityClass.EnsureSomething("mycustomerid", 4, myData.SomeProperty);
}
public void Foo2()
{
SomeUtilityClass.EnsureSomething("mycustomerid", 4, myData.SomeProperty);
}
}
、私は、ターゲットとしての性能を維持する必要があります。スレッドセーフな方法が必要です。この目的のために
は、私は小さなユーティリティクラスを書いた:
public sealed class CallHelper
{
private static readonly HashSet<int> g_YetCalled = new HashSet<int>();
private static readonly object g_SyncRoot = new object();
public static void EnsureOnce(Type type, Action a, params object[] arguments)
{
// algorithm for hashing adapted from http://stackoverflow.com/a/263416/588868
int hash = 17;
hash = hash * 41 + type.GetHashCode();
hash = hash * 41 + a.GetHashCode();
for (int i = 0; i < arguments.Length; i++)
{
hash = hash * 41 + (arguments[i] ?? 0).GetHashCode();
}
if (!g_YetCalled.Contains(hash))
{
lock (g_SyncRoot)
{
if (!g_YetCalled.Contains(hash))
{
a();
g_YetCalled.Add(hash);
}
}
}
}
}
のかかるコードは次のようになります。
public static class Program
{
static void Main()
{
SomeMethod("1", 1, 1);
SomeMethod("2", 1, 1);
SomeMethod("1", 1, 1);
SomeMethod("1", 1, null);
Console.ReadLine();
}
static void SomeMethod(string arg1, int arg2, object arg3)
{
CallHelper.EnsureOnce(typeof(Program),()=>
{
Console.WriteLine("SomeMethod called only once for {0}, {1} and {2}", arg1, arg2, arg3);
}, arg1, arg2, arg3);
}
}
期待通りの出力は、次のとおりです。
SomeMethod called only once for 1, 1 and 1
SomeMethod called only once for 2, 1 and 1
SomeMethod called only once for 1, 1 and
このアプローチに関するいくつかの質問があります:
- スレッドの安全性を確保するためにクラスを適切にロックしていると思いますが、正しいのですか?
HashSet<int>
で、ハッシュを計算する私の方法は正しいですか?私は特にnull
の取り扱いが正しいかどうか、そしてもし私がAction
代理人をこの方法で "ハッシュ"することができるかどうか疑問に思っています。- 私のメソッドは現在、静的メソッドのみをサポートしています。メモリを漏らさずに、インスタンスを互換性のあるメソッド(識別子を識別器としてインスタンスに追加する)に移動するにはどうすればよいですか?
- ユーティリティのメソッド(アクションを指定するだけ)にすべての引数を手動で渡すことを回避する方法はありますか(パフォーマンスの影響により)?私は、outermethodからの引数の欠落のために導入されたバグの多くを恐れています。
あなたの関数がvoid以外事前
この「EnsureOnce」テクニックはMemoizationと呼ばれています。検索するとGoogleにたくさんのものが見つかります。 –