Let's say that I am using a library that I have no control over whatsoever. This library exposes service that requires argument of certain class. Class is marked as sealed and has no interface.
tl;dr: How can I reimplement sealed class as interface?
Code example:
using System;
namespace IDontHaveControlOverThis
{
// Note no interface and the class is being sealed
public sealed class ArgumentClass
{
public String AnyCall() => "ArgumentClass::AnyCall";
}
public sealed class ServiceClass
{
public String ServiceCall(ArgumentClass argument) => $"ServiceClass::ServiceCall({argument.AnyCall()})";
}
}
namespace MyCode
{
// Composite pattern, basically I need: "is a ArgumentClass"
// Obviously doesn't work - can't extend from sealed class
public class MyArgumentClass : IDontHaveControlOverThis.ArgumentClass
{
private IDontHaveControlOverThis.ArgumentClass arg = new IDontHaveControlOverThis.ArgumentClass();
public String AnyCall() => $"MyArgumentCLass::AnyCall({arg.AnyCall()})";
}
}
public class Program
{
public static void Main()
{
// I don't have control over this
IDontHaveControlOverThis.ServiceClass service = new IDontHaveControlOverThis.ServiceClass();
//This obviously works
IDontHaveControlOverThis.ArgumentClass arg = new IDontHaveControlOverThis.ArgumentClass();
Console.WriteLine($"Result: {service.ServiceCall(arg)}");
// How to make this work?
IDontHaveControlOverThis.ArgumentClass myArg = new MyCode.MyArgumentClass();
Console.WriteLine($"Result: {service.ServiceCall(myArg)}");
}
}
Based on the code sample you show, the answer is you can't. You need to be able to modify the behavior of IDontHaveControlOverThis.ArgumentClass, by setting a property, or creating a new instance with different constructor parameters in order to modify the servicecall. (It now always returns the same string, so the servicecall is always the same)
If you are able to modify the behavior of the ArgumentClass by setting properties.
You could create wrappers for the sealed classes in your own code, and use that throughout your codebase.
public class MyArgumentClass
{
// TODO: Set this to a useful value of ArgumentClass.
internal IDontHaveControlOverThis.ArgumentClass InnerArgumentClass { get; }
public virtual string AnyCall() => "???";
}
public class MyServiceClass
{
private IDontHaveControlOverThis.ServiceClass innerServiceClass
= new IDontHaveControlOverThis.ServiceClass();
public virtual string ServiceCall(MyArgumentClass argument)
{
return innerServiceClass.ServiceCall(argument.InnerArgumentClass);
}
}
or
public class MyArgumentClass
{
public virtual string AnyCall() => "???";
}
public class MyServiceClass
{
private IDontHaveControlOverThis.ServiceClass innerServiceClass
= new IDontHaveControlOverThis.ServiceClass();
public string ServiceCall(MyArgumentClass argument)
{
var serviceArgument = Convert(argument);
return innerServiceClass.ServiceCall(serviceArgument);
}
private IDontHaveControlOverThis.ArgumentClass Convert(MyArgumentClass argument)
{
// TODO: implement.
}
}
The compiler error message
Cannot implicitly convert type 'MyCode.MyArgumentClass' to 'IDontHaveControlOverThis.ArgumentClass'
note: emphasis mine
should give you a hint as to what you can do
public class MyArgumentClass {
private IDontHaveControlOverThis.ArgumentClass arg = new IDontHaveControlOverThis.ArgumentClass();
public String AnyCall() => $"MyArgumentCLass::AnyCall({arg.AnyCall()})";
public static implicit operator IDontHaveControlOverThis.ArgumentClass(MyArgumentClass source) {
return source.arg;
}
}
So now your "wrapper" exposes the 3rd party dependency as needed
IDontHaveControlOverThis.ArgumentClass myArg = new MyCode.MyArgumentClass();
or directly
var myArg = new MyCode.MyArgumentClass();
Console.WriteLine($"Result: {service.ServiceCall(myArg)}");
Reference User-defined conversion operators (C# reference)
Which can allow for abstracting your code
namespace MyCode {
public interface IMyService {
String ServiceCall(MyArgumentClass argument);
}
public class MyServiceClass : IMyService {
public string ServiceCall(MyArgumentClass argument) {
IDontHaveControlOverThis.ServiceClass service = new IDontHaveControlOverThis.ServiceClass();
return service.ServiceCall(argument);
}
}
}
Related
I have an existing C# console application that takes arguments and based on the arguments
creates an instance of markets (UK, US, MX..) using dependency injection.
Each market class does a 'string GetData()', 'string ProcessData()' and 'bool ExportData()'.
The application was initially created for one eCommerce vendor's markets. Now I am told to modify it for a different vendor that does a different process. The high-level flow remains the same.
'GetData' to fetch records from DB,
'ProcessData' for any transformation or the likes
'ExportData'.
The difference is Getdata() pulls records from DB and maps to an object. I am planning to use Petapoco. 'ProcessData' might return a similar class. 'Exportdata' currently does an API call but for the new vendor, I have to write to a file.
I was reading up on patterns I am totally confused. At first, I thought I needed abstract factory pattern and now I think the factory method is what I should be using but I am not sure if I am doing it right. Need some guidance/review here. A sample cs file I created from my understanding of factory pattern. This code is based on the headfirst code samples.
using System;
using System.Collections.Generic;
using StatusExport.Models;
namespace factorymethod
{
class Program
{
static void Main(string[] args)
{
ClientFactory factory = null;
Console.WriteLine("Enter client code:");
string clientCode= Console.ReadLine();
switch (clientCode.ToLower())
{
case "costco":
factory = new CostcoFactory("accountname", "taskname");
break;
//NEw vendor might be added
//case "walmart"
//factory = new WalmartFactory("taskname", "type");
//break
default:
break;
}
bool status = factory.ProcessData();
Console.ReadKey();
}
}
abstract class Client
{
public abstract string AccountName { get; }
public abstract string Task { get; set; }
//More properties might be added. Some may not even be used by some of the new vendors. For example, Costco Might need accountname and task. Tomorrow if walmart comes, they might not need these two or may need task and a new property 'type'
public abstract List<T> GetData<T>();
public abstract List<T> ProcessData<T>();
public abstract bool ExportData();
}
class CostcoClient : Client
{
public override string AccountName { get; }
public override string Task { get; set; }
public CostcoClient(string accountName, string task)
{
AccountName = accountName;
Task = task;
}
public override List<DBRecord> GetData<DBRecord>() //DBRecord class is specific to Costco.
{
List<DBRecord> dbresult = new List<DBRecord>();
//dbresult = db return data mapped to an object DBRecord using petapoco. Another vendor might have a different class to which DB records are mapped. So the return type can be generic
return asn;
}
public override List<T> ProcessData<T>()
{
throw new NotImplementedException(); //Any data transformation or business logic. Return type might be DBRecord or a new class altogether
}
public override bool ExportData()
{
throw new NotImplementedException();//Call API or write data to file and if success send true else false
}
}
abstract class ClientFactory
{
public abstract bool ProcessData();
}
class CostcoFactory : ClientFactory
{
public string AccountName { get; }
public string Task { get; set; }
public CostcoFactory(string accountname, string task)
{
AccountName = accountname;
Task = task;
}
public override bool ProcessData()
{
CostcoClient gc = new CostcoClient(AccountName, Task);
var result = gc.GetData<DBRecord>();
return true;
}
}
}
Do you think this is the right design approach?
I also want to keep the console project independent of vendor project. So maybe 'StatusExport.Program' for the console application. DLL projects StatusExport.Common to hold the interface and abstract classes' and 'StatusExport.Client(ex:StatusExport.Costco)' for each vendor stuff.
You can create BaseClient class that will contains a basic group of properties, and if you need to add something new - just inherit it. You did right, but i think it's better to change public modifier to protected in your properties AccountName and Task, to give access to them only from child classes.
Actually, you can create a BaseClientModels (request/response) for each method if you are not sure that returning type List will be always actual.
Example:
public abstract class BaseClient
{
#region Properties : Protected
protected abstract string AccountName { get; }
protected abstract string Task { get; set; }
#endregion
#region Methods : Public
public abstract BaseGetDataResponseModel GetData(BaseGetDataRequestModel model);
public abstract BaseProcessDataResponseModel ProcessData(BaseProcessDataRequestModel model);
public abstract BaseExportDataResponseModel ExportData(BaseExportDataRequestModel model);
#endregion
}
public class BaseGetDataResponseModel { }
public class BaseGetDataRequestModel { }
public class BaseProcessDataResponseModel { }
public class BaseProcessDataRequestModel { }
public class BaseExportDataResponseModel { }
public class BaseExportDataRequestModel { }
Then let's look on your class CostcoClient and how it can looks like:
public class CostcoClient : BaseClient
{
#region Properties : Protected
protected override string AccountName { get; }
protected override string Task { get; set; }
protected virtual IDataReader<BaseGetDataRequestModel, BaseGetDataResponseModel> DataReader { get; }
protected virtual IDataProcessor<CostcoClientProcessDataRequestModel, CostcoClientProcessDataResponseModel> DataProcessor { get; }
protected virtual IExportDataHandler<CostcoClientExportDataRequestModel, CostcoClientExportDataResponseModel> ExportDataHandler { get; }
#endregion
#region Constructors
public CostcoClient(string accountName, string task)
{
//set DataReader, DataProcessor, ExportDataHandler
AccountName = accountName;
Task = task;
}
#endregion
#region Methods : Public
public override BaseGetDataResponseModel GetData(BaseGetDataRequestModel model)
{
if (model is CostcoClientGetDataRequestModel clientGetDataRequestModel)
{
return DataReader.ReadData(clientGetDataRequestModel);
}
return null; //wrong type has passed
}
public override BaseProcessDataResponseModel ProcessData(BaseProcessDataRequestModel model)
{
if (model is CostcoClientProcessDataRequestModel clientProcessDataRequestModel)
{
return DataProcessor.ProcessData(clientProcessDataRequestModel);
}
return null;
}
public override BaseExportDataResponseModel ExportData(BaseExportDataRequestModel model)
{
if (model is CostcoClientExportDataRequestModel clientExportDataRequestModel)
{
return ExportDataHandler.Handle(clientExportDataRequestModel);
}
return null;
}
#endregion
}
public class CostcoClientGetDataRequestModel : BaseGetDataRequestModel { }
public class CostcoClientGetDataResponseModel : BaseGetDataResponseModel { }
public class CostcoClientProcessDataRequestModel : BaseProcessDataRequestModel { }
public class CostcoClientProcessDataResponseModel : BaseProcessDataResponseModel { }
public class CostcoClientExportDataRequestModel : BaseExportDataRequestModel { }
public class CostcoClientExportDataResponseModel : BaseExportDataResponseModel { }
public interface IDataReader<TIn, TOut>
{
public TOut ReadData(TIn model);
}
public interface IDataProcessor<TIn, TOut>
{
public TOut ProcessData(TIn model);
}
public interface IExportDataHandler<TIn, TOut>
{
public TOut Handle(TIn model);
}
public class CostcoClientDataReader : IDataReader<CostcoClientGetDataRequestModel, CostcoClientGetDataResponseModel>
{
public CostcoClientGetDataResponseModel ReadData(CostcoClientGetDataRequestModel model)
{
throw new NotImplementedException();
}
}
//and so on
You have to implement IDataReader, IDataProcessor, IExportDataHandler, make your logic and call it from GetData, ProcessData, ExportData methods, as an example, and get instances via dependency injection.
Then, we can change your factory to this:
public interface IClientFactory
{
BaseClient GetClientService(ClientServicesEnum value);
}
public class BaseClientFactory : IClientFactory
{
#region Propertied : Protected
protected virtual IEnumerable<BaseClient> Services { get; }
protected string AccountName { get; }
protected string Task { get; set; }
#endregion
#region Constructors
public BaseClientFactory(IEnumerable<BaseClient> services, string accountname, string task)
{
Services = services;
AccountName = accountname;
Task = task;
}
#endregion
public BaseClient GetClientService(ClientServicesEnum value)
=> Services.First(x => x.GetType().Equals(GetClientServiceByCode()[value]));
private Dictionary<ClientServicesEnum, Type> GetClientServiceByCode()
=> new Dictionary<ClientServicesEnum, Type>()
{
{ ClientServicesEnum.CostcoClient, typeof(CostcoClient) }
};
}
public enum ClientServicesEnum
{
CostcoClient = 1,
Another2 = 2,
Another3 = 3
}
Where
protected virtual IEnumerable<BaseClient> Services { get; }
you can get via DI too, and then get correct ServiceHandler by enum.
And your main function to call all this:
switch (clientCode)
{
case 1:
baseClient = ClientFactory.GetClientService(ClientServicesEnum.CostcoClient);
break;
case 2:
baseClient = ClientFactory.GetClientService(ClientServicesEnum.Another2);
break;
default:
break;
}
bool status = baseClient.ProcessData(null); //your model
The main thing is - you can use more than one pattern, for example one from Creational patterns, and one from Structural.
If i need some help in code architecture i use this:
https://refactoring.guru/
I think, using this example you can remove properties AccountName and Task, because of request models in methods.
I seem to have some trouble understanding generics in c#.
Basically i have a base class called ConfigWorker and a bunch of sub classes which should all use their own config class deriving from BaseConfig.
The ConfigWorker class i want to use should be determined dynamically during runtime given the name of the class as a parameter.
I can instantiate the sub class given it's name, but no matter what i try, i can't get the casting to a sensible base class to work.
Here's my code:
namespace DocumentHandler
{
public class BaseConfig
{
}
public class ConfigWorker<T> where T : BaseConfig
{
public virtual void Work(T options)
{
}
}
public class Worker1 : ConfigWorker<Worker1.Config>
{
public class Config : BaseConfig
{
public string test = "";
}
public override void Work(Config options)
{
//do something
}
}
public class Worker2 : ConfigWorker<Worker2.Config>
{
public class Config : BaseConfig
{
public string test = "";
}
public override void Work(Config options)
{
//do something else
}
}
public class Test
{
public static BaseConfig config;
public static void test()
{
(Activator
.CreateInstance(Type.GetType("DocumentHandler.Worker2"))
as ConfigWorker<BaseConfig>)
.Work(config);
}
}
}
The crucial line is
(Activator
.CreateInstance(Type.GetType("DocumentHandler.Worker2"))
as ConfigWorker<BaseConfig>)
.Work(config);
The casting to ConfigWorker<BaseConfig> returns null, as the cast can not be performed.
Trying to simply cast to ConfigWorker does not compile as the type parameter is missing.
Anything else i can try? CreateInstance obviously just returns an object and i need to cast that to be able to call the Work method
Any help is appreciated.
An instance of Worker2 is not a ConfigWorker<BaseConfig>! It's a ConfigWorker<Worker2.Config>. These are two totally different types. Generic classes are invariant. Only interfaces and delegates can be co- or contra-variant.
In your example, ConfigWorker is even contra-variant in T, meaning you use T as the type of an input parameter to a method. So what you try is actually dangerous.
Imagine your line would work: you get an variable of type ConfigWorker<BaseConfig>, so you could rely on this instance having a method Work() which takes a BaseConfig (or something derived from it) as argument. So nothing could stop you from calling it like
worker.Work(new Worker1.Config());
Compiles fine. But wait a moment! Didn't your line state that worker is a Worker2? Worker2 instances can only handle Worker2.Config arguments!
You completely loose type safety this way (well, you would if it was allowed).
There is a flaw in your class design.
This looks like a good problem that factory pattern has good good solution for.
Here is a simplified solution
namespace DocumentHandler
{
public interface IBaseConfig
{
}
public class ConfiManager : IBaseConfig
{
}
public abstract class WorkerFactory
{
private readonly IBaseConfig _config;
protected WorkerFactory(IBaseConfig config)
{
this._config = config;
}
public virtual void Work()
{
}
}
public class Worker1 : WorkerFactory
{
private readonly IBaseConfig _config;
public Worker1(IBaseConfig config):base(config)
{
_config = config;
}
public string test = "";
public override void Work()
{
//do something
}
}
public class Worker2 : WorkerFactory
{
private readonly IBaseConfig _config;
public string test = "";
public Worker2(IBaseConfig config):base(config)
{
this._config = config;
}
public override void Work()
{
Console.WriteLine("Hello world");
}
}
public class Test
{
public static IBaseConfig config = new ConfiManager();
public static void test()
{
WorkerFactory worker =
(Worker2) Activator.CreateInstance(Type.GetType("DocumentHandler.Worker2"), config);
worker.Work();
}
}
}
In C# a static class can not derive from any other class besides object.
Currently I have this base class:
public static class BaseModule
{
public static string UsedSource {get; set;}
public static Write(string text)
{
OtherStaticClass.Log(UsedSource, text);
}
}
Now, depending on which class I'm using, I want to change UsedSource.
// this does not work
internal static class ModuleA : BaseModule
{
static ModuleA(){
UsedSource = "A" // just an example
}
}
// this does not work
internal static class ModuleB : BaseModule
{
static ModuleB(){
UsedSource = "B" // just an example
}
}
Supposed to be called like this
ModuleA.Write("Hi");
ModuleB.Write("Hi");
This approach does not work because a static class cannot derive from anything else than object.
Is there any other way to change the property?
You have a lot of static classes going on here and I'm not entirely sure they're necessary. My example does not use static classes other than for the OtherStaticClass reference you have. I understand this may not be quite what you're looking for; many ways to skin this cat.
public abstract class BaseModule
{
public string UsedSource { get; set; }
public void Write(string text)
{
OtherStaticClass.Log(UsedSource, text);
}
}
public class ModuleA : BaseModule
{
public ModuleA()
{
UsedSource = "A";
}
}
public class ModuleB : BaseModule
{
public ModuleB()
{
UsedSource = "B";
}
}
To get your output then, you just need to create new instances of ModuleA and ModuleB.
var moduleA = new ModuleA();
var moduleB = new ModuleB();
moduleA.Write("Hi");
moduleB.Write("Hi");
Using a static class means using a singleton. Singletons defeat the purpose of tracking the effective dependencies of your classes.
Anyway, you can approach the problem by refactoring your code and using a factory:
In this case, just drop the static keyword and let the class be inheritable (you have to add the appropriate virtual keywords to allow proper inheritance):
public class BaseModule
{
public string UsedSource {get; set;}
public Write(string text)
{
OtherStaticClass.Log(UsedSource, text);
}
}
Then, add an additional class which holds the reference (I gave useless names, focus on the purpose):
public static class MySingleton
{
public static BaseModule _Module;
public static BaseModule Module
{
get
{
return _Module;
}
}
public static void ChangeImplementation (BaseModule module)
{
// do your checks here
_Module = module;
}
}
This way wou can achieve what you ask.
As you can see, this code has several issues, among them it's important to note that this code has global side effects and is not thread safe.
A better approach is to have drop the singleton entirely, and pass the BaseModule class (that can be inherited) as an argument of methods/constructors when needed.
I don't see that you need more than one static class. Instead separate the logic into methods in one static class.
public static class Module
{
private const string SourceA = "A";
private const string SourceB = "B";
public static WriteA(string text)
{
Write(SourceA, text);
}
public static WriteB(string text)
{
Write(SourceB, text);
}
private static Write(string source, string text)
{
OtherStaticClass.Log(source, text);
}
}
Then instead of
ModuleA.Write("Hi");
ModuleB.Write("Hi");
you'd do
Module.WriteA("Hi");
Module.WriteB("Hi");
If you can't change the BaseModule class, you can use it with other state and recover state after using:
public static class BaseModule
{
public static string UsedSource {get; set;}
public static Write(string text)
{
OtherStaticClass.Log(UsedSource, text);
}
}
internal class Writer : IDisposable
{
string _lastSource;
public Writer(string source)
{
_lastSource = BaseModule.UsedSource;
BaseModule.UsedSource = source;
}
public void Dispose()
{
BaseModule.UsedSource = _lastSource;
}
}
internal abstract class Module
{
public abstract Source { get; };
public void Write(string text)
{
using (var writer = new Writer(Source))
{
BaseModule.Write(text);
}
}
}
internal class ModuleA : Module
{
public override Source => "A";
}
internal class ModuleB : Module
{
public override Source => "B";
}
But you must ensure thread safety.
If you can change the BaseModule class:
public static class BaseModule
{
public static Write(string text, string source)
{
OtherStaticClass.Log(source, text);
}
}
internal abstract class Module
{
public abstract Source { get; };
public void Write(string text)
{
BaseModule.Write(text, Source);
}
}
internal class ModuleA : Module
{
public override Source => "A";
}
internal class ModuleB : Module
{
public override Source => "B";
}
I currently have the following problem:
I have a class which includes 3 different fields
Enum x
ActiveDirectoryUser y
CustomClass z
The enum can be initialised by passing a string or the enum object.
The ADUser can be initialised by passing a string (LoginName) or the user by itself and the CustomClass can be initialized by passing a string, int or the object.
Now I want to initialize the class to pass all different combinations like
class(string enumValue, string adUser, string customClass)
class(string enumValue, ADUser adUser, CustomClass customClass)
class(EnumValue enumValue, string adUser, CustomClass customClass)
Is there a way to simplify the constructors without typing all of the 12 possibilities (Enum-2 * ADUser-2 * CClass-3 = 12)?
I thought about chained constructors where i also ended up with 12 constructors but also thought about just passing the c# Object on each parameter and cast it and do stuff with it but i think that is just a dirty workaround?
Edit
The class is contained in an library and so can be used internal but also public. For the internal uses there is no problem to pass a concrete version of an object.
But if i use it public in other solutions these solutions can only refer to string or int values. So the class should be able to 'take' the values and convert them while beeing initialised because it have access to all the real objects.
Maybe this clarifies the problem a bit.
Here some code snippets with changed names:
#region Content of libraryOne
public class ClassName
{
internal EnumValueWrapper { get; set; }
internal CustomClass { get; set; }
internal ADUser { get; set; }
public ClassName() { ... } //Now via Builder Pattern
internal ClassName() { ... } //With Parameters for internal initialisations
public InformationContainer GetContentInfo()
{
//[...]Process Stuff and Return Container
}
}
internal CustomClass { ... }
internal EnumValueWrapper { ... }
internal ADUser { ... }
#endregion Content of libraryOne
If your class has only 3 properties (EnumValue, ADUser, CustomClass) then you should have only one constructor with these :class(EnumValue enumValue, ADUser adUser, CustomClass customClass). The ADUser and CustomClass should be instantiated outside of your class using their constructor which support string or int, etc;
Example:
class (EnumValue param, new ADUser(string_param), new CustomClass(int_param));
class (EnumValue param, new ADUser(ADUser_param), new CustomClass(string_param));
Edit
You can use it like I described above for internal scope and for the public part you can use and expose a factory (wrapper) class which actually can receive users and other parameters as strings or int and internally instantiate and return your class.
In addition to your snippet: Create a proxy like public class in your assembly that can be accessed from outside (from other assemblies).Make your class internal:
public class ClassNameBuilder
{
private ClassName _className;
public ClassNameBuilder(string enumValue, string user, string custom_class)
{
_className = new ClassName(EnumToString, new User(user), new CustomClass(custom_class));
}
public void CallClassNameMethod1()
{
return _className.Method1()
}
public void CallClassNameMethod2()
{
return _className.Method2()
}
}
The builder class can use whatever method you want to build the ClassName object; This way you can expose all your class methods without using multiple constructors.
I think the best thing to do is use the Builder pattern. You can even use it with derived classes.
My classes to build:
public class MyBaseClass
{
public MyBaseClass(SomeEnum enumValue, User user)
{
}
}
public class MyDerivedClass : MyBaseClass
{
public MyDerivedClass(SomeEnum enumValue, User user, CustomClass customStuff)
: base(enumValue, user)
{
}
}
Now let's add a builder class featuring an additional extension class for making things much more comfortable (it's sort of an extended Builder pattern using C# extension method wizadry):
public class MyBaseClassBuilder
{
public SomeEnum EnumValue { get; set; }
public User User { get; set; }
}
public static class MyBaseClassBuilderExtensions
{
public static T SetEnumValue<T>(this T instance, SomeEnum value)
where T : MyBaseClassBuilder
{
instance.EnumValue = value;
return instance;
}
public static T SetEnumValue<T>(this T instance, string value)
where T : MyBaseClassBuilder
{
instance.EnumValue = (SomeEnum)Enum.Parse(typeof(SomeEnum), value);
return instance;
}
public static T SetUser<T>(this T instance, User value)
where T : MyBaseClassBuilder
{
instance.User = value;
return instance;
}
public static T SetUser<T>(this T instance, string value)
where T : MyBaseClassBuilder
{
instance.User = new User(value);
return instance;
}
public static MyBaseClass Build(this MyBaseClassBuilder instance)
{
return new MyBaseClass(instance.EnumValue, instance.User);
}
}
Now let's do the same thing for our derived class:
public class MyDerivedClassBuilder : MyBaseClassBuilder
{
public CustomClass CustomStuff { get; set; }
}
public static class MyDerivedClassBuilderExtensions
{
public static T SetCustomStuff<T>(this T instance, CustomClass value)
where T : MyDerivedClassBuilder
{
instance.CustomStuff = value;
return instance;
}
public static T SetCustomStuff<T>(this T instance, string value)
where T : MyDerivedClassBuilder
{
instance.CustomStuff = new CustomClass(value);
return instance;
}
public static MyDerivedClass Build(this MyDerivedClassBuilder instance)
{
return new MyDerivedClass(instance.EnumValue, instance.User, instance.CustomStuff);
}
}
Now you can construct your instances in some fluent API style way:
static void Main(string[] args)
{
MyBaseClass baseInstance = new MyBaseClassBuilder()
.SetEnumValue("Alpha")
.SetUser("Big Duke")
.Build();
MyDerivedClass derivedInstance = new MyDerivedClassBuilder()
.SetEnumValue(SomeEnum.Bravo)
.SetUser(new User("Lt. Col. Kilgore"))
.SetCustomStuff("Smells like victory")
.Build();
}
Finally the additional types:
public enum SomeEnum
{
Alpha,
Bravo
}
public class User
{
public User(string name)
{
this.Name = name;
}
public string Name { get; private set; }
}
public class CustomClass
{
public CustomClass(string notation)
{
this.Notation = notation;
}
public string Notation { get; private set; }
}
This way you can construct instances which require many constructor arguments in a comfortable way.
I have the following classes, and when I call CreateQuerySettings on the BaseScriptConfigurationList, it returns the new QuerySettings from ConfigurationList, rather than the HierarchicalQuerySettings value in BaseScriptConfigurationList:
public abstract class ConfigurationList<TConfigurationObject, TPropertyEnum>
{
public QuerySettings<TConfigurationObject, TPropertyEnum> CreateQuerySettings()
{
return new QuerySettings<TConfigurationObject, TPropertyEnum>();
}
}
public class BaseScriptConfigurationList : EditableConfigurationList<BaseScriptConfiguration, BaseScriptConfiguration.Property>
{
public BaseScriptConfigurationList(ConfigurationManager configurationManager)
: base(configurationManager, InternalAdminObjectType.BaseScript)
{
_BaseScriptPageListWatcher = new ConfigurationList<BaseScriptPageConfiguration, BaseScriptPageConfiguration.Property>.
ConfigurationWatcher(null);
_ConfigurationWatcher.ChildWatchers.Add(_BaseScriptPageListWatcher);
}
public new QuerySettings<BaseScriptConfiguration, BaseScriptConfiguration.Property> CreateQuerySettings()
{
return new HierarchicalQuerySettings<BaseScriptConfiguration, BaseScriptConfiguration.Property, BaseScriptQueryChildrenSettings>();
}
}
Edit: I make the call from another class where TConfigurationObjectList is BaseScriptConfigurationList. I've added the constructor to the code above so you can see what it's doing. Please note that EditableConfigurationList inherits from ConfigurationList.
TConfigurationObjectList cl = (TConfigurationObjectList)typeof(TConfigurationObjectList).GetConstructor(new Type[] { typeof(ConfigurationManager) }).Invoke(new object[] { Manager.ConfigurationManager });
var querySettings = cl.CreateQuerySettings();
When I make this call, it goes into the ConfigurationList.CreateQuerySettings method.
How can I hide the CreateQuerySettings method, so that when I call it from the BaseScriptConfigurationList class, I get a HierarchicalQuerySettings object?
The new modifier can be beasty. Note that you are hiding and not overriding in your example. You are not showing that part of the code, but I assume you have this situation:
class Base
{
public static void BaseMethod() { Console.WriteLine("BASE!"); }
}
class Derived : Base
{
// Hides Base.BaseMethod()
new public static void BaseMethod() { Console.WriteLine("DERIVED!"); }
}
Base a = new Base();
a.BaseMethod(); // -> "BASE!"
Base b = new Derived();
b.BaseMethod(); // -> "BASE!"
Derived b = new Derived();
b.BaseMethod(); // -> "DERIVED!"
In BaseScriptConfigurationList.CreateQuerySettings()
you're return type is QuerySettings<T,T> so you will always get that type as a return value, but you are returning a HierarchicalQuerySettings. You can one, change the return type of CreateQuerySettings() to HierarchicalQuerySettings or two, cast the object to its child type "HierarchicalQuerySettings". If you really want to hide it, you can do this:
public class newclass : BaseScriptConfigurationList
{
public new HierarchicalQuerySettings<BaseScriptConfiguration, BaseScriptConfiguration.Property> CreateQuerySettings()
{
return (HierarchicalQuerySettings<BaseScriptConfiguration, BaseScriptConfiguration.Property>)base.CreateQuerySettings();
}
}
But that doesn't really seem efficient and i advise against it. Like i said, i maybe missing some other requirement, but based on the info that you gave..
Basically, what I'm seeing (and making assumptions) that TConfigurationObjectList Inhertis from ConfigurationList somewhere along the lines, so on and so forth, all the way up to EditableConfigurationList. since you are dynamically creating an instance of the class TConfigurationObjectList, and calling the method from that point, you will be calling the base ConfigurationList member CreateQuerySettings. You do not have access to the new CreateQuerySettings. If you are creating the class BaseScriptConfigurationList instance at this point, cast the object ((BaseScriptConfigurationList)cl).CreateQuerySettings(). That being said. if you do not know what you have at runtime:
var obj = typeof(TConfigurationObjectList).GetConstructor(new Type[] { typeof(ConfigurationManager) }).Invoke(new object[] { Manager.ConfigurationManager });
var cl = (obj as BaseScriptConfigurationList) ?? (TConfigurationObjectList)obj;
// or do something else
var querySettings = cl.CreateQuerySettings();
Note i am assuming your architecture is roughly set up like this:
public abstract class ConfigurationList<TConfigurationObject, TPropertyEnum>
{
public QuerySettings<TConfigurationObject, TPropertyEnum> CreateQuerySettings()
{
return new QuerySettings<TConfigurationObject, TPropertyEnum>();
}
}
public class TConfigurationObjectList : ConfigurationList<BaseScriptConfiguration, BaseScriptConfiguration.Property>
{
}
public class EditableConfigurationList<T, T1> : TConfigurationObjectList
{
protected EditableConfigurationList(ConfigurationManager configurationManager, object baseScript)
{
throw new NotImplementedException();
}
}
public class BaseScriptConfigurationList : EditableConfigurationList<BaseScriptConfiguration, BaseScriptConfiguration.Property>
{
public BaseScriptConfigurationList(ConfigurationManager configurationManager)
: base(configurationManager, InternalAdminObjectType.BaseScript)
{
}
public new QuerySettings<BaseScriptConfiguration, BaseScriptConfiguration.Property> CreateQuerySettings()
{
return new HierarchicalQuerySettings<BaseScriptConfiguration, BaseScriptConfiguration.Property, BaseScriptQueryChildrenSettings>();
}
}
public class QuerySettings<T, T1>
{
}
public class HierarchicalQuerySettings<T, T1, T2> : QuerySettings<BaseScriptConfiguration, BaseScriptConfiguration.Property>
{
}
public class BaseScriptQueryChildrenSettings
{
}
public class BaseScriptPageConfiguration
{
public class Property
{
}
}
public class InternalAdminObjectType
{
public static object BaseScript { get; set; }
}
public class ConfigurationManager
{
}
public class BaseScriptConfiguration
{
public class Property
{
}
}
Create a base interface for the ConfigurationList class (say IConfigurationList) and use this interface as the data type for the variable cl instead of TConfigurationList.