14

我们需要在代码中使用具有静态方法的非托管库。我想在我的代码中引入库操作作为依赖项。除了拥有静态方法之外,该库还有一个初始化方法和一个设置方法,它们都是全局的。所以我不能把它包装在一个实例类中,因为如果一个实例改变了一个设置,所有其他实例都会受到影响,如果一个实例被初始化,所有其他实例都会被重新初始化。

我想把它作为一个单例类来介绍。这样它将在一个实例类中,但只有一个实例,因此我不必担心更改设置或初始化。您如何看待这种方法?我对依赖注入模式很陌生,我不确定单例模式是否是一个好的解决方案?对于类似的情况,您的解决方案是什么?

编辑:初始化也需要一个参数,所以我不能只锁定方法调用并在每次调用时重新初始化和更改设置。

编辑 2:以下是一些方法的签名:

public static void Initialize(int someParameter)
// Parameter can only be changed by re-initalization which
// will reset all the settings back to their default values.

public static float[] Method1(int someNumber, float[] someArray)

public static void ChangeSetting(string settingName, int settingValue)
4

2 回答 2

10

如果您只需要在启动时设置一次设置,那么我建议制作一个非静态包装类,该类在其自己的静态构造函数中完成静态类的所有初始化。这样您就可以放心,它只会发生一次:

public class MyWrapper
{
    public MyWrapper()
    {
        // Do any necessary instance initialization here
    }

    static MyWrapper()
    {
        UnManagedStaticClass.Initialize();
        UnManagedStaticClass.Settings = ...;
    }

    public void Method1()
    {
        UnManagedStaticClass.Method1();
    }
}

但是,如果您需要在每次调用它时更改设置,并且您想让您的实例线程安全,那么我建议您锁定一个静态对象,这样您就不会在它们运行时意外覆盖静态设置仍在被另一个线程使用:

public class MyWrapper
{
    public MyWrapper()
    {
        // Do any necessary instance initialization here
    }

    static MyWrapper()
    {
        UnManagedStaticClass.Initialize();
    }

    static object lockRoot = new Object();

    public void Method1()
    {
        lock (lockRoot)
        {
            UnManagedStaticClass.Settings = ...;
            UnManagedStaticClass.Method1();
        }
    }
}   

如果您需要将初始化参数传递给类的实例构造函数,那么您也可以通过使用静态标志字段来做到这一点:

public class MyWrapper
{
    public MyWrapper(InitParameters p)
    {
        lock (lockRoot)
        {
            if (!initialized)
            {
                UnManagedStaticClass.Initialize(p);
                initialized = true;
            }
        }
    }

    static bool initialized = false;
    static object lockRoot = new Object();

    public void Method1()
    {
        lock (lockRoot)
        {
            UnManagedStaticClass.Settings = ...;
            UnManagedStaticClass.Method1();
        }
    }
}

如果您还需要每次重新初始化,但您担心性能因为重新初始化太慢,那么唯一的其他选择(除了可怕的单例)是自动检测是否需要重新初始化和只有在必要时才这样做。至少那时,唯一会发生的情况是两个线程同时使用两个不同的实例。你可以这样做:

public class MyWrapper
{
    public MyWrapper(InitParameters initParameters, Settings settings)
    {
        this.initParameters = initParameters;
        this.settings = settings;
    }

    private InitParameters initParameters;
    private Settings settings;
    static MyWrapper currentOwnerInstance;
    static object lockRoot = new Object();

    private void InitializeIfNecessary()
    {
        if (currentOwnerInstance != this)
        {
            currentOwnerInstance = this;
            UnManagedStaticClass.Initialize(initParameters);
            UnManagedStaticClass.Settings = settings;
        }
    }

    public void Method1()
    {
        lock (lockRoot)
        {
            InitializeIfNecessary();
            UnManagedStaticClass.Method1();
        }
    }
}
于 2013-02-15T13:06:10.637 回答
1

我将使用无状态服务类,并在每个方法调用中传递静态类的状态信息。在不知道您的课程的任何细节的情况下,我将仅用 ac# 静态类展示另一个示例。

public static class LegacyCode
{
    public static void Initialize(int p1, string p2)
    {
        //some static state
    }
    public static void ChangeSettings(bool p3, double p4)
    {
        //some static state
    }
    public static void DoSomething(string someOtherParam)
    {
        //execute based on some static state
    }
}

public class LegacyCodeFacadeService
{
    public void PerformLegacyCodeActivity(LegacyCodeState state, LegacyCodeParams legacyParams)
    {
        lock (_lockObject)
        {
            LegacyCode.Initialize(state.P1, state.P2);
            LegacyCode.ChangeSettings(state.P3, state.P4);
            LegacyCode.DoSomething(legacyParams.SomeOtherParam);
            //do something to reset state, perhaps
        }
    }
}

您必须稍微填写一些空白,但希望您能明白这一点。关键是在所需的最少时间内在静态对象上设置状态,并在整个时间内锁定对它的访问,因此其他调用者不会受到全局状态更改的影响。您必须创建此类的新实例才能使用它,因此它是完全可注入和可测试的(提取接口的步骤除外,为简洁起见,我跳过了该步骤)。

这里有很多实现选项。例如,如果您必须对 LegacyCodeState 进行大量更改,但仅更改为少数特定状态,则可以使用重载来管理这些状态。

编辑

这在很多方面都比单例更可取,最重要的是,您将无法累积并耦合到全局状态:如果它是静态类的唯一入口点,这会将全局状态转换为非全局状态. 但是,如果您最终需要一个单例,您可以通过在此处封装构造函数来轻松切换。

public class LegacyCodeFacadeService
{
    private LegacyCodeFacadeService() { }

    public static LegacyCodeFacadeService GetInstance()
    {
        //now we can change lifestyle management strategies later, if needed
        return new LegacyCodeFacadeService();
    }

    public void PerformLegacyCodeActivity(LegacyCodeState state, LegacyCodeParams legacyParams)
    {
        lock (_lockObject)
        {
            LegacyCode.Initialize(state.P1, state.P2);
            LegacyCode.ChangeSettings(state.P3, state.P4);
            LegacyCode.DoSomething(legacyParams.SomeOtherParam);
            //do something to reset state, perhaps
        }
    }
}
于 2013-02-15T13:33:44.460 回答