我正在设计一个类库,它有一堆类型的方法“EnsureXXX”。只要调用代码需要某些东西而不需要特定于参数的初始化,就会调用此方法的思想。它类似于ASP.Net的EnsureChildControls
方法,但参数为鉴别符。
前:
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
代表。提前致谢
答案 0 :(得分:5)
这基本上是memoize,除了你的函数是无效的。但是,对输入参数进行比较的相同考虑因素仍然有效。
Wes Dyer讨论how to make a generic, multi-argument memoize in this post。一般的想法是将所有参数都转换为匿名类型,并将其用作字典键。
关于使这个线程安全,请考虑.NET already has concurrent collections。您不需要将非并发集合转换为并发集合;只需使用提供的集合。
要使实例方法在没有泄漏的情况下工作,要么保留WeakReference实例,要么在实例本身中存储memoizer的实例,无论哪种方法都适合您。