C# 重构静态类以与依赖注入一起使用
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/14894980/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Refactoring a static class to use with dependency injection
提问by hattenn
We need to use an unmanaged library in our code that has static methods. I'd like to introduce the library operation as a dependency in my code. And apart from having static methods, the library has an initialization method and a settings method, both are global. So I can't just wrap this in an instance class, because if one instance changes a setting, all other instances will be affected, and if one instance gets initialized, all other instances will be reinitialized.
我们需要在我们的代码中使用一个具有静态方法的非托管库。我想在我的代码中引入库操作作为依赖项。除了有静态方法外,库还有一个初始化方法和一个设置方法,两者都是全局的。所以我不能仅仅把它包装在一个实例类中,因为如果一个实例更改了一个设置,所有其他实例都会受到影响,如果一个实例被初始化,所有其他实例将被重新初始化。
I thought about introducing it as a singleton class. This way it will be in an instance class, but there will only be one instance thus I won't have to worry about changing the settings or initialization. What do you think about this approach? I'm pretty new to the dependency injection pattern and I'm not sure if the singleton pattern is a good solution? What would your solution be to a similar case?
我考虑将其作为单例类引入。这样它将在一个实例类中,但只有一个实例,因此我不必担心更改设置或初始化。您如何看待这种方法?我对依赖注入模式很陌生,我不确定单例模式是否是一个好的解决方案?对于类似的情况,您的解决方案是什么?
Edit:The initialization takes a parameter too, so I can't just lock the method calls and re-initialize and change settings every time it is called.
编辑:初始化也需要一个参数,所以我不能只锁定方法调用并在每次调用时重新初始化和更改设置。
Edit 2:Here are the signatures of some methods:
编辑 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)
采纳答案by Steven Doggart
If you only need to set the settings once at start up, then I would recommend making a non-static wrapper class which does all the initialization of the static class in its own static constructor. That way you can be assured that it will only happen once:
如果您只需要在启动时设置一次设置,那么我建议创建一个非静态包装类,它在其自己的静态构造函数中完成静态类的所有初始化。这样你就可以放心,它只会发生一次:
public class MyWrapper
{
public MyWrapper()
{
// Do any necessary instance initialization here
}
static MyWrapper()
{
UnManagedStaticClass.Initialize();
UnManagedStaticClass.Settings = ...;
}
public void Method1()
{
UnManagedStaticClass.Method1();
}
}
However, if you need to change the settings each time you call it, and you want to make your instances thread-safe, then I would recommend locking on a static object so that you don't accidentally overwrite the static settings while they're still in use by another thread:
但是,如果您每次调用它时都需要更改设置,并且希望使您的实例线程安全,那么我建议锁定静态对象,这样您就不会在静态设置发生时意外覆盖它们仍在被另一个线程使用:
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();
}
}
}
If you need to pass initialization parameters into your class's instance constructor, then you could do that too by having a static flag field:
如果您需要将初始化参数传递给类的实例构造函数,那么您也可以通过使用静态标志字段来做到这一点:
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();
}
}
}
If you also need to re-initialize each time, but you are concerned about performance because re-initializing is too slow, then the only other option (outside of the dreaded singleton) is to auto-detect if you need to re-initialize and only do it when necessary. At least then, the only time it will happen is when two threads are using two different instances at the same time. You could do it like this:
如果您每次还需要重新初始化,但您担心性能因为重新初始化太慢,那么唯一的其他选择(在可怕的单例之外)是自动检测是否需要重新初始化和只在必要时才这样做。至少,只有当两个线程同时使用两个不同的实例时才会发生这种情况。你可以这样做:
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();
}
}
}
回答by tallseth
I would use a stateless service class, and pass in state info for the static class with each method call. Without knowing any details of you class, I'll just show another example of this with a c# static class.
我将使用无状态服务类,并在每个方法调用中传递静态类的状态信息。在不知道你的类的任何细节的情况下,我将用 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
}
}
}
You'll have to fill in the blanks a little bit, but hopefully you get the idea. The point is to set state on the static object for the minimum amount of time needed, and lock access to it that entire time, so no other callers can be affected by your global state change. You must create new instances of this class to use it, so it is fully injectable and testable (except the step of extracting an interface, which I skipped for brevity).
您将不得不填写一些空白,但希望您能理解。关键是在所需的最短时间内在静态对象上设置状态,并在整个时间内锁定对它的访问,因此其他调用者不会受到您的全局状态更改的影响。您必须创建此类的新实例才能使用它,因此它是完全可注入和可测试的(除了提取接口的步骤,为了简洁我跳过了该步骤)。
There are a lot of options in implementation here. For example, if you have to change LegacyCodeState a lot, but only to a small number of specific states, you could have overloads that do the work of managing those states.
这里的实现有很多选择。例如,如果您必须大量更改 LegacyCodeState,但只更改少数特定状态,则可以使用重载来完成管理这些状态的工作。
EDIT
编辑
This is preferable to a singleton in a lot of ways, most importantly that you won't be able to accumulate and couple to global state: this turns global state in to non-global state if it is the only entry point to your static class. However, in case you do end up needing a singleton, you can make it easy to switch by encapsulating the constructor here.
这在很多方面都比单例更可取,最重要的是您将无法累积并耦合到全局状态:如果全局状态是静态类的唯一入口点,这会将全局状态转换为非全局状态. 但是,如果您最终需要单例,您可以通过在此处封装构造函数来轻松切换。
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
}
}
}