簡體   English   中英

如何確保每個參數組合只執行一次方法邏輯?

[英]How to ensure a method logic is executed only once per arguments combination?

我正在設計一個類庫,它有一堆類型的方法“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. 有沒有辦法避免手動將所有參數傳遞給實用程序方法(只是指定操作),而無需探索堆棧跟蹤(由於性能影響)? 我擔心由於外部方法缺少參數而引入了很多錯誤。

提前致謝

這基本上是一個memoize ,除了你的函數是無效的。 但是,對輸入參數進行比較的相同考慮因素仍然有效。

Wes Dyer討論了如何在這篇文章中創建一個通用的多參數memoize 一般的想法是將所有參數都轉換為匿名類型,並將其用作字典鍵。

關於使這個線程安全,請考慮.NET已經有並發集合 您不需要將非並發集合轉換為並發集合; 只需使用提供的集合。

為了使這個方法適用於沒有泄漏的實例方法,要么將WeakReference保留給實例,要么將memoizer的實例存儲在實例本身中,無論哪種方法都適合您。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM