简体   繁体   中英

How to properly share base class static property with derived classes

I have a base class that contains the basic logic to perform an http request. However I need to have some kind of switch because dependending on the configuration set by the user, the domain of the url will change.

Based on that I created a static property that holds an enum responsible to give me the base value I need. On top of that, the base class will be distribuited via nuget package, so it is somewhat sealed to the users, that need only to implement its required fields and can use any logic defined on its parent.

So basically I came up with this solution so far.

public abstract class Base{
    protected static Environment Environment { get; set; }
    public static Init(Environment NewEnvironment){
        Environment = NewEnvironment;
    }
    public void UseEnvironment(){
        //use the selected environment on the method
    }
}

public A : Base{ 
    public void UseAEnvironment(){
        UseEnvironment(); //using the environment defined with A.init() call
    }
}
public B : Base{ 
    public void UseBEnvironment(){
        UseEnvironment(); //using the environment defined with B.init() call
    }

I know that there is only one copy of the static property in memory, thus when you set it to a value for the A class, B will end up using the same value.

I need to be able to do

A.Init(Environment.Debug);
B.Init(Environment.Release);

So when I run the program, all methods defined in class A will run with the Debug value, while class B will have the Release value.

My solution does not do what I need, is there a way to make it work, or is there any better architecture decisions to avoid this situation and accomplish a similar result?

If you have:

public abstract class Base<T> where T : Base<T>
{
  protected static Environment Environment { get; private set; }

  public static void Init(Environment newEnvironment)
  {
    Environment = newEnvironment;
  }
}

And then:

public class A : Base<A>
{
  ...
}

public class B : Base<B>
{
  ...
}

then you can just do:

Base<A>.Init(Environment.Debug);
Base<B>.Init(Environment.Release);

It works because each substitution of something for T in Base<T> has its own static members. That is, each constructed generic type ("closed" generic type) has separate static fields.

You could also write it as:

A.Init(Environment.Debug);
B.Init(Environment.Debug);

but I would consider that slightly confusing, even if it is a more compact syntax.

It seems like a bit of an odd design. Maybe something like compiler directives (#if DEBUG) or configuration via App.config or similar would be better suited?

Anyway, if not .. something like the following should work

public abstract class Base<T> where T : Base<T>
{
    private static readonly IDictionary<Type, Environment> _Environments = new Dictionary<Type, Environment>();

    public static void Init(Environment NewEnvironment)
    {
        _Environments[typeof(T)] = NewEnvironment;
    }

    protected Environment GetEnvironment()
    {
        if (!_Environments.ContainsKey(typeof(T)))
            return default(Environment);

        return _Environments[typeof(T)];
    }

}

public class A : Base<A> {
   // ...
}
public class B : Base<B> {
    // ...
}

I do not like the following proposed code but it represents the smallest amount of code upheaval to provide what I think you are trying to do. Note to dropped static declarations in the abstract base class.

public abstract class Base {
    protected Environment Environment { get; set; }

     public Init(Environment NewEnvironment) {
        Environment = NewEnvironment;
    }
}

public A : Base{ 
    public void UseEnvironment() {
     }
}
public B : Base{ 
    public void UseEnvironment() {
     }
}

Then initialise.

static A DebugHttpAccess;
static B RealeaseHttpAccess;

DebugHttpAccess = new A();
DebugHttpAccess.Init(Environment.Debug);
RealeaseHttpAccess= new B();
RealeaseHttpAccess.Init(Environment.Release);

Finally use as dictated by other higher level logic:

if ( needDebugHttpTracing )
    DebugHttpAccess.UseEnvironment();
else
    ReleaseHttpAccess.UseEnvironment();

I suspect the proper solution to your requirement involves inversion of control and a container that can manage the life time of your Http access as a singleton class. The container would inject the appropriate Http access instance as defined by other process-wide configuration settings.

See autofac.org for an example of an IOC container.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM