如何确保每个参数组合只执行一次方法逻辑
本文关键字:执行 一次 方法 参数 何确保 确保 组合 | 更新日期: 2023-09-27 18:20:10
我正在设计一个类库,它有一堆类似于"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
委托- 我的方法目前只支持静态方法。如何在不泄漏内存的情况下转移到与实例兼容的方法(将实例添加为鉴别器)
- 是否有任何方法可以避免手动将所有参数传递给实用程序方法(仅指定操作),而不探究堆栈竞争(因为性能影响)?我担心会因为路由方法中缺少一个参数而引入很多错误
提前感谢
这本质上是一个内存化,只是你的函数是void。但是,对输入参数进行比较的同样考虑仍然有效。
Wes Dyer在这篇文章中讨论了如何制作一个通用的、多参数的备忘录。一般的想法是将所有参数滚动到一个匿名类型中,并将其用作字典键。
关于使该线程安全,请考虑.NET已经有并发集合。您不需要将非并发集合转换为并发集合;只需使用提供的集合即可。
为了使实例方法在不泄漏的情况下工作,可以保留对实例的WeakReference,也可以将memoizer的实例存储在实例本身中,无论哪种方法对您有效。