Reputation: 195
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?
Upvotes: 0
Views: 2058
Reputation: 2243
In my case, I also arrived at the generic class trick first but favoring composition over inheritance helped me in keeping the code more flexible and readable. So the following program, where the Environment
variable is shared:
public enum Environment { Debug, Release }
public abstract class Base {
protected static Environment Environment { get; set; }
protected static void Init(Environment NewEnvironment) { Environment = NewEnvironment; }
}
public class A : Base {
public static void Init() { Init(Environment.Debug); }
public static void PrintEnvironment() { Console.WriteLine(Environment); }
}
public class B : Base {
public static void Init() { Init(Environment.Release); }
public static void PrintEnvironment() { Console.WriteLine(Environment); }
}
void Main() {
A.Init();
B.Init();
A.PrintEnvironment(); // Prints: Release
B.PrintEnvironment(); // Prints: Release
}
Was first replaced to:
public enum Environment { Debug, Release }
public abstract class Base<T> where T : Base<T> {
protected static Environment Environment { get; set; }
protected static void Init(Environment NewEnvironment) { Environment = NewEnvironment; }
}
public class A : Base<A> {
public static void Init() { Init(Environment.Debug); }
public static void PrintEnvironment() { Console.WriteLine(Environment); }
}
public class B : Base<B> {
public static void Init() { Init(Environment.Release); }
public static void PrintEnvironment() { Console.WriteLine(Environment); }
}
void Main() {
A.Init();
B.Init();
A.PrintEnvironment(); // Prints: Debug
B.PrintEnvironment(); // Prints: Release
}
And ended up being:
public enum Environment { Debug, Release }
public sealed class Base {
public Environment Environment { get; set; }
public void Init(Environment NewEnvironment) { Environment = NewEnvironment; }
}
public class A {
public static Base MyBase = new Base();
public static void Init() { MyBase.Init(Environment.Debug); }
public static void PrintEnvironment() { Console.WriteLine(MyBase.Environment); }
}
public class B {
public static Base MyBase = new Base();
public static void Init() { MyBase.Init(Environment.Release); }
public static void PrintEnvironment() { Console.WriteLine(MyBase.Environment); }
}
void Main() {
A.Init();
B.Init();
A.PrintEnvironment(); // Prints: Debug
B.PrintEnvironment(); // Prints: Release
}
Upvotes: 0
Reputation: 61952
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.
Upvotes: 3
Reputation: 1780
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.
Upvotes: 0
Reputation: 5734
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> {
// ...
}
Upvotes: 1