如何确保每个参数组合只执行一次方法逻辑

本文关键字:执行 一次 方法 参数 何确保 确保 组合 | 更新日期: 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

我有一些与这种方法有关的问题:

  1. 我认为我已经正确地锁定了类以确保线程安全,但我是对的吗
  2. HashSet<int>和我计算哈希的方法正确吗?我特别想知道null处理是否正确,以及我是否可以用这种方式"散列"Action委托
  3. 我的方法目前只支持静态方法。如何在不泄漏内存的情况下转移到与实例兼容的方法(将实例添加为鉴别器)
  4. 是否有任何方法可以避免手动将所有参数传递给实用程序方法(仅指定操作),而不探究堆栈竞争(因为性能影响)?我担心会因为路由方法中缺少一个参数而引入很多错误

提前感谢

如何确保每个参数组合只执行一次方法逻辑

这本质上是一个内存化,只是你的函数是void。但是,对输入参数进行比较的同样考虑仍然有效。

Wes Dyer在这篇文章中讨论了如何制作一个通用的、多参数的备忘录。一般的想法是将所有参数滚动到一个匿名类型中,并将其用作字典键。

关于使该线程安全,请考虑.NET已经有并发集合。您不需要将非并发集合转换为并发集合;只需使用提供的集合即可。

为了使实例方法在不泄漏的情况下工作,可以保留对实例的WeakReference,也可以将memoizer的实例存储在实例本身中,无论哪种方法对您有效。