Multi-level inheritance with fluent interface in C# - c#

Given the sample console application below:
QUESTION #1: Why does .Name() return typeof OranizationBuilder, but .Write() calls CorporationBuilder?
QUESTION #2: How to get .Name() to return typeof CorporationBuilder?
namespace MyCompany
{
using System;
class Program
{
static void Main(string[] args)
{
Console.WriteLine(Environment.NewLine);
Factory.Organization()
.ID(33)
.Name("Oranization A")
.Write();
Console.WriteLine("\n----------------------------\n");
Factory.Corporation()
.Date(DateTime.Today) // Pass
.ID(44)
.Name("Company B")
// .Date(DateTime.Today) // Fail
.Write();
// QUESTION #1: Why does .Name() return typeof OranizationBuilder,
// but .Write() calls CorporationBuilder?
// QUESTION #2: How to get .Name() to return typeof CorporationBuilder?
Console.ReadLine();
}
}
/* Business Classes */
public abstract class Contact
{
public int ID { get; set; }
}
public class Organization : Contact
{
public string Name { get; set; }
}
public class Corporation : Organization
{
public DateTime Date { get; set; }
}
/* Builder */
public abstract class ContactBuilder<TContact, TBuilder>
where TContact : Contact
where TBuilder : ContactBuilder<TContact, TBuilder>
{
public ContactBuilder(TContact contact)
{
this.contact = contact;
}
private TContact contact;
public TContact Contact
{
get
{
return this.contact;
}
}
public virtual TBuilder ID(int id)
{
this.Contact.ID = id;
return this as TBuilder;
}
public virtual void Write()
{
Console.WriteLine("ID : {0}", this.Contact.ID);
}
}
public class OrganizationBuilder : ContactBuilder<Organization, OrganizationBuilder>
{
public OrganizationBuilder(Organization contact) : base(contact) { }
public virtual OrganizationBuilder Name(string name)
{
(this.Contact as Organization).Name = name;
return this;
}
public override void Write()
{
base.Write();
Console.WriteLine("Name : {0}", this.Contact.Name);
}
}
public class CorporationBuilder : OrganizationBuilder
{
public CorporationBuilder(Corporation contact) : base(contact) { }
public virtual CorporationBuilder Date(DateTime date)
{
// Cast is required, but need this.Contact to be typeof 'C'
(this.Contact as Corporation).Date = date;
return this;
}
public override void Write()
{
base.Write();
Console.WriteLine("Date : {0}", (this.Contact as Corporation).Date.ToShortDateString());
}
}
/* Factory */
public class Factory
{
public static OrganizationBuilder Organization()
{
return new OrganizationBuilder(new Organization());
}
public static CorporationBuilder Corporation()
{
return new CorporationBuilder(new Corporation());
}
}
}
EDIT/UPDATE
Here's my first attempt at a solution (see below), although I'm stuck inside the Factory and not sure how to configure the .Organization() and .Corporation() method types.
namespace MyCompany
{
using System;
class Program
{
static void Main(string[] args)
{
Console.WriteLine(Environment.NewLine);
Factory.Organization()
.ID(33)
.Name("Oranization A")
.Write();
Console.WriteLine("\n----------------------------\n");
Factory.Corporation()
.ID(44)
.Name("Company B")
.Date(DateTime.Today)
.Write();
Console.ReadLine();
}
}
/* Business Classes */
public abstract class Contact
{
public int ID { get; set; }
}
public class Organization : Contact
{
public string Name { get; set; }
}
public class Corporation : Organization
{
public DateTime Date { get; set; }
}
/* Builder */
public abstract class ContactBuilder<TContact, TBuilder>
where TContact : Contact
where TBuilder : ContactBuilder<TContact, TBuilder>
{
public ContactBuilder(TContact contact)
{
this.contact = contact;
}
private TContact contact;
public TContact Contact
{
get
{
return this.contact;
}
}
public virtual TBuilder ID(int id)
{
this.Contact.ID = id;
return this as TBuilder;
}
public virtual void Write()
{
Console.WriteLine("ID : {0}", this.Contact.ID);
}
}
public class OrganizationBuilder<TOrganization, TBuilder> : ContactBuilder<TOrganization, TBuilder> where TOrganization : Organization where TBuilder : OrganizationBuilder<TOrganization, TBuilder>
{
public OrganizationBuilder(TOrganization contact) : base(contact) { }
public virtual TBuilder Name(string name)
{
this.Contact.Name = name;
return this as TBuilder;
}
public override void Write()
{
base.Write();
Console.WriteLine("Name : {0}", this.Contact.Name);
}
}
public class CorporationBuilder<TCorporation, TBuilder> : OrganizationBuilder<TCorporation, TBuilder> where TCorporation : Corporation where TBuilder : CorporationBuilder<TCorporation, TBuilder>
{
public CorporationBuilder(TCorporation contact) : base(contact) { }
public virtual TBuilder Date(DateTime date)
{
this.Contact.Date = date;
return this as TBuilder;
}
public override void Write()
{
base.Write();
Console.WriteLine("Date : {0}", this.Contact.Date.ToShortDateString());
}
}
/* Factory */
public class Factory
{
public static OrganizationBuilder<Organization, OrganizationBuilder> Organization()
{
return new OrganizationBuilder<Organization, OrganizationBuilder>(new Organization());
}
public static CorporationBuilder<Corporation, CorporationBuilder> Corporation()
{
return new CorporationBuilder<Corporation, CorporationBuilder>(new Corporation());
}
}
}
Here's the specific problem area:
/* Factory */
public class Factory
{
public static OrganizationBuilder<Organization, OrganizationBuilder> Organization()
{
return new OrganizationBuilder<Organization, OrganizationBuilder>(new Organization());
}
public static CorporationBuilder<Corporation, CorporationBuilder> Corporation()
{
return new CorporationBuilder<Corporation, CorporationBuilder>(new Corporation());
}
}
How to configure the OrganizationBuilder and CorportationBuilder?

When Name returns a reference, it returns this - so when the instance is actually an instance of CorporationBuilder, that reference is returned as normal. Just because the method is declared to return OrganizationBuilder doesn't mean it only returns an OrganizationBuilder reference. It returns a reference to an instance of OrganizationBuilder instance or a derived class (or null, of course).
When the Write method is then called, that's a virtual method so the execution-time type of the object is checked to find the implementation to use. The execution-time type is still CorporationBuilder, so the override specified in that type is used.
As for how to make Name() return the appropriate type - that would require more generics, basically. It can be done, but it's a pain - I've done something similar in Protocol Buffers, but it's not pleasant. You'd make OrganizationBuilder generic in TContact and TBuilder as well, and make Name return TBuilder via a cast from this to TBuilder. Then CorporationBuilder would either be generic too, or just inherit from OrganizationBuilder<Corporation, CorporationBuilder>.
EDIT: Yes, I see the problem (which I'd forgotten about before). You may want to have a concrete non-generic class called CorporationBuilder as well, to avoid the recursive generics:
public class OrganizationBuilder :
OrganizationBuilder<Organization, OrganizationBuilder>
You might also want to rename OrganizationBuilder to OrganizationBuilderBase to avoid confusion :)
(You don't need CorporationBuilder to be generic itself, if it's at the bottom of the hierarchy.)
However, this is getting extremely complicated. You might want to at least consider avoiding inheritance here. Scrap the generics, and make OrganizationBuilder have a CorporationBuilder instead of deriving from it.
Basically this pattern always gets complicated after a while - you end up needing every level to be generic apart from the leaf nodes, which always need to be nongeneric to avoid the recursion problem you've already seen. It's a pain.

The .Name() function in the OrganizationBuilder has a signature to return OrganizationBuilder type - no matter on which derived object it is called from. That is why you see it returning OrganizationBuilder. If you would have override Name() function in your contract builder and set the name to something else, you will notice that the Name() function is acting on your runtime object.
Now if you want to know how to make Name() return the builder that you want, you should follow the same technique as you used for ID() method.
EDIT/UPDATE:
Well, now I don't understand the actual error you are facing - with the new updates. Can you share the exact error you are facing?
On a side note: I feel this design is totally convoluted. I wouldn't give this to my consumers just to support a nice pattern of a builder method returning the appropriate builder object. I'd stick to much simpler approach.

Related

Which design pattern to use when we have classes that does similar high level functionality but the returns different types in methods?

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.

Intelligent Generic Static Method

I wrote C# code as described below that inherits a class from a generic class with static methods. I want to call the child class for its static methods (inherited from the base class) without having to specify the type.
EDITED! More "real" code
public class Rec
{
public string Name { get; set; }
public override string ToString() { return this.Name; }
public virtual void Load() { /* HERE IT READS A TEXT FILE AND LOAD THE NAME */ }
}
public class BaseClass<T> : Rec
{
public T Argument { get; set; }
public override void Load() { /* NOW IT LOADS ALSO THE ARGUMENT */ }
public static H Method<H>() where H : Rec, new()
{
H iH = new H();
iH.Load();
iH.Name += " " + iH.Argument.ToString();
return iH;
}
}
public class Child : BaseClass<string> { }
public class SomeOtherClass
{
public void Test()
{
Child i = Child.Method();
//instead of Child.Method<Child>();
}
}
So, instead of having to call method<h>() i'd like to just call method(), so the code should assume that "h" is the caller type. Like in:
How can I do it?
Static methods are not inherited in C#
See this answer for an idea of a potential implementation: Stack Overflow whats-the-correct-alternative-to-static-method-inheritance
You could change method<h> to method and make it an instance method:
public class BaseClass<T> where T, new()
{
public T method() { /* RETURN SOMETHING */ }
}
And then call it as follows:
public class ABC : Child
{
public void Test()
{
var iABC = this.method();
}
}

Generic conversion issue

I'm trying to design a pattern to orchest several operations. Each operation would take a parameter and deliver a result. That result might or might not be used by the following operation. This is a simplified version of the design, but if you copy/paste this on a console projecto it will "work" (there's a compiling error I can't get fixed).
Error
The type
'ConsoleApplication1.InternalDebit'
cannot be used as type parameter 'T1' in the generic type or method
'ConsoleApplication1.Orchestrator.Add(T1)'. There is no implicit
reference conversion from
'ConsoleApplication1.InternalDebit'
to
'ConsoleApplication1.Operation'. c:\projects\BCP\BaseMvc\ConsoleApplication1\ConsoleApplication1\Program.cs 17 13 ConsoleApplication1
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
var internalDebit = new InternalDebit<InternalDebitParameter, InterbankCreditParameter>(new InternalDebitParameter() { Id = 1 });
var orchestrator = new Orchestrator();
// error here!
orchestrator.Add(internalDebit);
}
}
public interface IParameter
{
}
public interface IResult
{
}
public interface IReversible
{
void Reverse();
}
public interface IOperation<T, R>
where T : class, IParameter
where R : class, IResult
{
Type ParameterType { get; }
Type ResultType { get; }
T Parameter { get; set; }
R Execute(T parameter);
}
public abstract class Operation<T, R> : IOperation<T, R>
where T : class, IParameter
where R : class, IResult
{
public virtual R Execute(T parameter)
{
this.Parameter = parameter;
return default(R);
}
public Type ParameterType
{
get { return typeof(T); }
}
public Type ResultType
{
get { return typeof(R); }
}
public T Parameter { get; set; }
public Operation(T parameter)
{
this.Parameter = parameter;
}
}
public class InternalDebitParameter : IParameter
{
public int Id { get; set; }
}
public class InterbankCreditParameter : IParameter, IResult
{
public int Id { get; set; }
}
public class InternalDebit<T, R> : Operation<T, R>
where T : class, IParameter
where R : class, IResult
{
public InternalDebit(T parameter)
: base(parameter)
{
}
public override R Execute(T parameter)
{
return new InterbankCreditParameter() { Id = 2 } as R;
}
}
public class Orchestrator
{
public List<Operation<IParameter, IResult>> Operations { get; private set; }
public List<IParameter> Parameters { get; private set; }
public void Add<T1>(T1 t) where T1 : Operation<IParameter, IResult>
{
this.Operations.Add(t);
}
public void SetUpParameters(params IParameter[] parameters)
{
this.Parameters = new List<IParameter>();
parameters.ToList().ForEach(s => this.Parameters.Add(s));
}
public void Play()
{
IParameter generalResult = null;
foreach (var instrument in this.Operations)
{
var parameter = this.Parameters.FirstOrDefault(s => s.GetType() == instrument.ParameterType);
if (parameter == null)
{
IResult actualResult = null;
if (generalResult != null)
{
try
{
actualResult = instrument.Execute(generalResult);
}
catch (Exception ex)
{
if (instrument is IReversible)
((IReversible)instrument).Reverse();
else
throw;
break;
}
finally
{
if (actualResult is IParameter)
generalResult = (IParameter)actualResult;
}
}
else
{
throw new Exception("Orchetrator missconfiguration.");
}
}
}
}
}
}
If you play a little with covariance/contravariance you may be able to do something similar to what you're after. Or anyway, the compiler will tell you more precisely where what you're trying to do is not type-safe.
First step: the error you're getting states that There is no implicit reference conversion from 'InternalDebit<InternalDebitParameter,InterbankCreditParameter>' to 'Operation<IParameter,IResult>'.
So, since InternalDebit implements IOperation, the first thing you can do is make IOperation covariant, trying to define it as:
public interface IOperation<out T, out R>
This would mean that a variable of type IOperation<IParameter,IResult> would happily accept a value of type Operation<InternalDebitParameter,InterbankCreditParameter>, which is one step closer to what you want.
You would then have your Add's method signature constrained in terms of IOperation instead of Operation
public void Add<T1>(T1 t) where T1 : IOperation<IParameter, IResult>
The compiler tells us something's wrong:
Invalid variance: The type parameter 'T' must be invariantly valid on 'IOperation<T,R>.Parameter'. 'T' is covariant.
Invalid variance: The type parameter 'T' must be contravariantly valid on 'IOperation<T,R>.Execute(T)'. 'T' is covariant.
That's the first indication of why this code is unsound. Covariant parameters can only be used "on the way out" of function (i.e. as a return type), not as "in" parameters.
Second step making IOperation covariant. This may be painful and change your code, as it means changing Execute not to accept parameters of type T.
public interface IOperation<out T, out R>
where T : class, IParameter
where R : class, IResult
{
Type ParameterType { get; }
Type ResultType { get; }
T Parameter { get; /*set;*/ } //can't allow the interface to set T
// R Execute(T parameter); // can't have an Execute with T as a parameter
R Execute(); // you can however inject T in the constructor of the
// inherited class and call Execute without parameters
}
Third step you now get a new error:
The best overloaded method match for 'System.Collections.Generic.List<Operation<IParameter,IResult>>.Add(Operation<IParameter,IResult>)' has some invalid arguments
This is again a covariance issue. List is not covariant and you can't Add t to a List.
I don't really know what to suggest,since I don't want to change completely the intent of your code (especially since I can't say I fully understand it...)
You may find something useful in this answer, for instance:
Covariance and IList
You're taking generics too far into C++ templating power. On the line that gives the error you're implicitly creating the function:
public void Add(InternalDebit<InternalDebitParameter, InterbankCreditParameter>);
As declared, this class inherits from:
Operation<InternalDebitParameter, InterbankCreditParameter>
The generic requirement howeveer states that T1 should be of type Operation<IParameter, IResult>, which it isn't, even though both parameters do inherit from the correct types, since there is no polymorphism allowed.
What you're trying to achieve here is inherently impossible with generics (or templates in C++ actually) because you are specifying way too much, and specifying inheritance requirements that can never be satisfied. You need to remember that generics are in a way just a luxury shorthand of writing many classes with only a little bit of code, they do not introduce recursive polymorphism all of a sudden.
Long story short, rewrite the code to use inheritance and base classes rather than depending on generics. I suspect your entire pattern is possible without a single generic and just as type safe.
Ok, for the sake of completeness of this post, I'll show you how I finally get this working.
It can be better, I'm still open to suggestions. Unfortunatelly I got to move on from this task, it's already delayed.
I'll post and edit to this answer in order to follow up it on Code Review site.
Copy/Paste in a console application, it's a fully functional code example.
class Program
{
static void Main(string[] args)
{
var transferenceInfo = new InterbankTranferenceInfo();
var orchestrator = new Orchestrator(new InternalDebitOperation(transferenceInfo),
new InterbankCreditOperation(),
new CommissionOperation());
orchestrator.Run();
}
}
public class InterbankTranferenceInfo : IParameter
{
public bool InternalDebitDone { get; set; }
public bool InterbankCreditDone { get; set; }
public bool CommissionDone { get; set; }
}
public class InternalDebitOperation : Operation<InterbankTranferenceInfo>, IOperation<InterbankTranferenceInfo>
{
public InternalDebitOperation(InterbankTranferenceInfo parameter)
: base(parameter)
{
}
public override InterbankTranferenceInfo Execute()
{
return new InterbankTranferenceInfo() { InternalDebitDone = true };
}
}
public class InterbankCreditOperation : Operation<InterbankTranferenceInfo>, IOperation<InterbankTranferenceInfo>
{
public override InterbankTranferenceInfo Execute()
{
Parameter.InterbankCreditDone = true;
return Parameter;
}
}
public class CommissionOperation : Operation<InterbankTranferenceInfo>, IReversible, IOperation<InterbankTranferenceInfo>
{
public override InterbankTranferenceInfo Execute()
{
Parameter.CommissionDone = true;
// Uncomment this code to test Reverse operation.
// throw new Exception("Test exception, it should trigger Reverse() method.");
return Parameter;
}
public void Reverse()
{
Parameter.CommissionDone = false;
}
}
public enum OperationStatus
{
Done,
Pending,
Reversed
}
public interface IParameter
{
}
public interface IReversible
{
void Reverse();
}
public interface IOperation<out T> : IInternalOperation<T> where T : IParameter
{
}
public interface IInternalOperation<out T> : IExecutableOperation<T>
{
bool GetParameterFromParentOperation { get; }
OperationStatus Status { get; set; }
IParameter Execute(IParameter parameter);
}
public interface IExecutableOperation<out T>
{
T Execute();
}
//[System.Diagnostics.DebuggerStepThroughAttribute()]
public abstract class Operation<T> : IInternalOperation<T> where T : IParameter
{
public T Parameter { get; private set; }
public bool GetParameterFromParentOperation { get { return this.Parameter == null; } }
public OperationStatus Status { get; set; }
public Operation()
{
Status = OperationStatus.Pending;
}
public Operation(IParameter parameter)
{
Status = OperationStatus.Pending;
this.Parameter = (T)parameter;
}
public abstract T Execute();
public virtual IParameter Execute(IParameter parameter)
{
this.Parameter = (T)parameter;
return this.Execute();
}
}
public class Orchestrator
{
public List<IOperation<IParameter>> Operations { get; private set; }
public Orchestrator(params IOperation<IParameter>[] operations)
{
this.Operations = new List<IOperation<IParameter>>();
foreach (var item in operations)
{
this.Operations.Add((IOperation<IParameter>)item);
}
}
public IParameter Run()
{
IParameter previousOperationResult = null;
foreach (var operation in this.Operations)
{
try
{
if (operation.GetParameterFromParentOperation)
previousOperationResult = operation.Execute(previousOperationResult);
else
previousOperationResult = operation.Execute();
operation.Status = OperationStatus.Done;
}
catch (Exception)
{
foreach (var o in this.Operations)
{
if (o is IReversible)
{
((IReversible)o).Reverse();
o.Status = OperationStatus.Reversed;
}
else
throw;
}
break;
}
}
return previousOperationResult;
}
}
EDIT
Code Review Post

How to get type of a derived class from another derived class

I have the (pseudo) code:
public class GlobalClass
{
public GlobalClass()
{
var x = this.GetType().Name // Returns "Channels"
// WHAT TO DO HERE?
}
}
public class BaseClass
{
public string Title { get; set; }
}
And using this code:
public class Channels : GlobalClass
{
public Channels()
{
}
public class Channel : BaseClass
{
}
}
Where the comment is (// WHAT TO DO HERE?), I want to get the runtime type of BaseClass,
where in my sample code should return Channel.
I am open to different approaches, but only if it's accompanied with an explanation why I should change the code.
I think you need a generic class here, something like:
public class GlobalClass<T> where T : BaseClass
{
public GlobalClass()
{
var theType = typeof(T); //you got it
}
}
public class BaseClass
{
public string Title { get; set; }
}
public class Channel : BaseClass { }
public class Channels : GlobalClass<Channel> { }
You can use reflection like this:
using System.Reflection;
...
public class GlobalClass
{
public GlobalClass()
{
Type[] types = Assembly.GetExecutingAssembly ().GetTypes ();
foreach ( Type t in types )
{
if ( t.BaseType == typeof ( BaseClass ) )
{
Console.WriteLine ( "I found a class " + t.Name + " that subclass BaseClass" );
}
}
}
}
See also Stack Overflow question List of classes in an assembly.
is operator is just for that purpose.
getType() method with class Type can also be used.
class Example
{
static void ShowTypeInfo (object o)
{
Console.WriteLine ("type name = {0},
full type name = {1}", o.GetType(),
o.GetType().FullName );
}
public static void Main()
{
long longType = 99;
Example example= new Example();
ShowTypeInfo (example);
ShowTypeInfo (longType);
}
}
To get the runtime type of anything, you first need an object instance to get the type from. So with your given structure, that's not possible.
There are two possible approaches:
Add a BaseClass parameter to the constructor of your GlobalClass:
public class GlobalClass
{
public GlobalClass(BaseClass data)
{
var dataType = data == null ? null : data.GetType();
// do something with the type
}
}
public class Channels : GlobalClass
{
public Channels(Channel data) : base(data)
{
}
public class Channel : BaseClass
{
}
}
Pass the type to the constructor directly:
public class GlobalClass
{
public GlobalClass(Type actualType)
{
Debug.Assert(typeof(BaseClass).IsAssignableFrom(actualType));
}
}
public class Channels : GlobalClass
{
public Channels() : base(typeof(Channel))
{
}
public class Channel : BaseClass
{
}
}
If the structure for some reason doesn't allow generics here (as Danny Chen suggested), I'd personally prefer the second approach, since that doesn't need an actual instance.

Call constant property on class like static?

I got an abstract base class
public class Base
{
public abstract String Info { get; }
}
and some children.
public class A : Base
{
public override String Info { get { return "A does ..."; } }
}
public class B : Base
{
public override String Info { get { return "B does ..."; } }
}
This is mere a constant but I want to make sure using Base that all classes implement it.
Now I sometimes do not have an object instance but want to access A.Info - this is not possible due it is a instance property.
Is there another way than implementing the same property on instance AND on static level? That would be feel like a duplicate violating DRY programming style.
NEW EDIT: I now see this two solutions:
public class Base
{
public abstract String ClassInfo { get; }
}
public class A : Base
{
public override String ClassInfo { get { return Info; } }
public static String Info { get { return "A does ..."; } }
}
public class B : Base
{
public override String ClassInfo { get { return Info; } }
public static String Info { get { return "In B we do ..."; } }
}
With this I can do with any object of type Base something like object.ClassInfo but also use the value in my factory hardcoded like if(A.Info) return new A(). But I have to implement two properties for the same information in every class.
On the other hand:
public class Base
{
public abstract String ClassInfo { get; }
public static String GetClassInfo<T>() where T : BaseControl, new()
{
T obj = new T();
return obj.ClassInfo;
}
}
public class A : Base
{
public override String ClassInfo { get { return "text A"; } }
}
public class B : Base
{
public override String ClassInfo { get { return "text B"; } }
}
Due to the abstract Base it is made sure that ClassInfo is always implemented. Calls with obj.ClassInfo and Base.GetClassInfo<A>() are okay. But with this every child of Base must have a default constructor without arguments and we loose performance with the unneccessary created instance.
Is there any other idea? Which one would you prefer and why?
If you need specific return results of your static properties, you're better of either
a) Instance properties
2) Attributes
In the example you've already given, you've got an instance of Base, which means you can just make the instance property virtual:
public class Base
{
public virtual string Info { get { return "From Base"; } }
}
public class A : Base
{
public override string Info { get { return "From A"; } }
}
If you wanted to go the attribute route, you define it as such:
[AttributeUsage(AttributeTargets.Class, Inherited = true)]
public class InfoAttribute : Attribute
{
public InfoAttribute(string info) { this.Info = info; }
public string Info { get; private set; }
}
[InfoAttribute(Info = "From Base")]
public class Base
{
public string GetInfo()
{
var attr = GetType()
.GetCustomAttributes(typeof(InfoAttribute), true)
.FirstOrDefault();
return (attr == null) ? null : attr.Info;
}
}
[InfoAttribute(Info = "From A")]
public class A : Base { }
If you wanted to call it as a static function call, you could make this change:
public static string GetInfo(Base instance)
{
var attr = instance.GetType()
.GetCustomAttributes(typeof(InfoAttribute), true)
.FirstOrDefault();
return (attr == null) ? null : attr.Info;
}
And then call it as: Base.GetInfo(instance);. All in all, not very elegant!
This is not possible.
static members cannot be virtual or abstract.
You should make an abstract instance property.
Statics can't be overridden. If you truly want to do something like that, you'd want an instance property that is virtual in the base that gets overridden in the subclasses.
Does it compiled? I don't think so. Static cannot be marked as override, virtual or abstract.

Categories