Как обеспечить соответствие логики метода выполняется только один раз для каждой комбинации аргументов?

Я разрабатываю библиотеку классов, которая имеет набор методов типа "EnsureXXX". Идея этих методов состоит в том, чтобы вызывать всякий раз, когда вызывающий код требует чего-то, кроме инициализации, специфичной для аргументов. Он похож на метод EnsureChildControls в ASP.Net, но с аргументами в качестве дискриминаторов.

Пример:

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. I думаю, что я правильно заблокировал класс, чтобы обеспечить безопасность потоков, но прав ли я?
  2. Правильны ли HashSet и мой метод вычисления хэша? Мне особенно интересно, правильна ли обработка null , и могу ли я "хешировать" Action делегировать таким образом.
  3. Мои методы в настоящее время поддерживают только статические методы. Как я могу перейти к методу, совместимому с экземпляром (добавив экземпляр в качестве дискриминатора), без утечки памяти?
  4. Есть ли способ избежать передачи всех аргументов в служебный метод вручную (просто указав действие) без изучения трассировки стека ( из-за влияния на производительность)? Боюсь, что появилось много ошибок из-за отсутствия аргументов внешнего метода.

Заранее спасибо

6
задан Steve B 12 January 2012 в 13:13
поделиться