Generic container: why do I need an interface? - c#

Say I have the following
public interface IInterval<T>
{
T Start { get; }
T Stop { get; }
}
public class DateTimeInterval : IInterval<DateTime>
{
private DateTime _start;
private DateTime _stop;
public DateTimeInterval(DateTime start, DateTime stop)
{
_start = start; _stop = stop;
}
public DateTime Start
{
get { return _start; }
}
public DateTime Stop
{
get { return _stop; }
}
}
public class SortedIntervalList<T>
where T : IInterval<T>, IComparable<T>
{
}
If I were to now try to instantiate the container
var test = new SortedIntervalList<DateTimeInterval>();
I get a compilation error
The type 'Test' cannot be used as type parameter 'T' in the generic
type or method TestContainer<T>. There is no implicit reference
conversion from 'Test' to ITest<Test>.
Why is this?
Note on edit history
For clarity, classes for the original question are included below
public interface ITest<T>
{
int TestMethod();
}
public class Test : ITest<bool>
{
public int TestMethod()
{
throw new NotImplementedException();
}
}
public class TestContainer<T>
where T : ITest<T>
{
}

where T : ITest<T>
Your class inherits ITest<bool>, which is not ITest<T> for your T (Test).
As the error is trying to tell you, that does not meet your generic constraint, so you can't do that.

Because you expect your T in TestContainer<T> to be ITest<T>. that doesn't make sense. I think you meant :
public class TestContainer<C, T>
where C : ITest<T>
{
}
For your updated code in your question:
public class SortedIntervalList<C, T>
where C : IInterval<T>, IComparable<T>
{ }
With:
test = new SortedIntervalList<DateTimeInterval, DateTime>();

Related

Cannot implicitly convert A<B> to A<C<D>> where B inherit from C<D>

I'm building a sort of library to perform text replacement in a document based on some rule. We built a POC and now I'm trying to create a library as generic as possible.
I have just one problem with inheritance:
This is the simplified representation of the classes/interfaces I'm dealing with:
public interface IRule {}
public interface IReplaceRule<T> : IRule
{
T ReplaceValue { get; set; }
}
public class CachedRules<T> where T : IReplaceRule<object>
{
#region Props
public T RuleTemplate { get; set; }
public IDictionary<string, T> RuleList { get; private set; } = null;
#endregion
public void SetRuleList(IDictionary<string, T> ruleList) { ... }
public bool ContainsRuleByKey(string key) { ... }
public bool TryGetRuleValueByKey(string key, out T rule) { ... }
}
public class SingleRowRule : IReplaceRule<string> { ... }
I also have a class which is like a repository of rules, and inside it I can add as many CachedRules as I need:
public class RulesStorage : AbstractRulesStorage
{
private CachedRules<SingleRowRule> singleRowRules;
public RulesStorage() { ... }
// Bunch of methods not useful for this question
// Here I need to return a list of ChachedRule, but just ofr testing I tried to return only one
public CachedRules<IReplaceRule<object>> GetCachedReplaceRules()
{
return singleRowRules;
}
}
Inside this class I need a method to return all the CachedRules declared in the RulesStorage:
Unfortunately the RulesStorage.GetCachedReplaceRules method give me this error:
Cannot implicitly convert type TestLib.Model.CachedRules<TestLib.Rules.SingleRowRule> to TestLib.Model.CachedRules<TestLib.Abstractions.IReplaceRule<object>
I really don't like the fact that I had to put <object> since IReplaceRule requires a generic and also I'm stuck because I don't know how to return this list of CachedRules without getting this compilation error.
Do you have some idea? Do I have to organize the code differently in your opinion?
Hope I've made myself clear and thanks in advance!
Instead of doing IReplaceRule<object> you can do it the way IEnumerable<T> inherits from IEnumerable. With that minor tweak in place, I create an implicit converter to go from T to IReplaceRule and the constraint in place now ensures I can actually do this safely.
I'm assuming you have a reason to have private CachedRules<SingleRowRule> singleRowRules; and can't just using private CachedRules<IReplaceRule> singleRowRules; which would remove the need for this extra conversion hop.
Code:
public interface IReplaceRule : IRule { object ReplaceValue { get; set; } }
public interface IReplaceRule<T> : IReplaceRule { new T ReplaceValue { get; set; } }
public class CachedRules<T> where T : IReplaceRule
{
public IDictionary<string, T> RuleList { get; private set; } = new Dictionary<string, T>();
//The key ingredient for a nice experience instead of just doing this in the method
public static implicit operator CachedRules<IReplaceRule>(CachedRules<T> rules)
=> new CachedRules<IReplaceRule> { RuleList = rules.RuleList.ToDictionary(x => x.Key, x => x.Value as IReplaceRule) };
}
public class SingleRowRule : IReplaceRule<string>
{
public string ReplaceValue { get; set; }
object IReplaceRule.ReplaceValue { get => ReplaceValue; set => ReplaceValue = value as string; }
}
public class RulesStorage
{
private CachedRules<SingleRowRule> singleRowRules = new CachedRules<UserQuery.SingleRowRule>();
//FIXME: just for testing purposes
public RulesStorage() => singleRowRules.RuleList.Add("Hello", new SingleRowRule { ReplaceValue = "World" });
// Here I need to return a list of ChachedRule, but just ofr testing I tried to return only one
public CachedRules<IReplaceRule> GetCachedReplaceRules() => singleRowRules;
}

Is this the only way to have the functionality of abstract static methods?

I'm working on a framework right now and the motto is "no redundancy" and "I don't want to know the vendor specifics" so most things are handled through Interfaces and Generic classes. Now I had the situation where I have an abstract class that wants to match things depending on it's own Enum variable se but it shouldn't have to know how the vendor provides a relatable variable to be matched to se. The vendor could have decided an integer, an Enum or a string would be the best to save that information but honestly I don't want to know.
So I thought well no problem have an abstract static method that must be provided by every implementation of a wrapper to compare se with the vendor specific way of saving that information.
//The original version I wanted to be possible
public abstract class AbstractGenericClass<TWrapper<T>, T> where TWrapper : AbstractGenericWrapper<T> {
protected TWrapper tWrapper;
//our SomeEnum se is somehow relatable to every T
//but we don't want to know how
protected SomeEnum se = ...;
//called on Start
public void Start() {
List<T> ts = FindObjectsOfType<T>;
foreach (T t in ts) {
if(T.Compare(t, this.se)) {
tWrapper = new TWrapper(t);
}
}
}
}
public abstract class AbstractGenericWrapper<T> {
T _t;
public AbstractGenericWrapper(T t) {
_t = t;
}
public static abstract bool Compare(T t, SomeEnum someEnum);
}
public class ConcreteNongenericWrapper : AbstractGenericWrapper<VendorSpecificImplementation> {
public static bool Compare(VendorSpecificImplementation t, SomeEnum someEnum) {
return t.vendorVariable.toLower().Equals(Enum.GetValues(typeof(someEnum), someEnum));
}
}
public class OtherConcreteNongenericWrapper : AbstractGenericWrapper<OtherVendorSpecificImplementation> {
public static bool Compare(OtherVendorSpecificImplementation t, SomeEnum someEnum) {
return t.otherVendorVariable % 3 == (int) someEnum;
}
}
public class SomeImplementation {
public static void main() {
AbstractGenericClass<ConcreteNongenericWrapper<ConcreteNongeneric>, ConcreteNongeneric> foo
= new AbstractGenericClass<ConcreteNongenericWrapper<ConcreteNongeneric>, ConcreteNongeneric>();
AbstractGenericClass<OtherConcreteNongenericWrapper<OtherConcreteNongeneric>, OtherConcreteNongeneric> bar
= new AbstractGenericClass<OtherConcreteNongenericWrapper<OtherConcreteNongeneric>, OtherConcreteNongeneric>();
foo.Start();
bar.Start();
}
}
I found out that that isn't possible and I wanted to know if this version down below is the best/only way of doing it? It has redundancy and I don't like it and it is longer.
//An attempt at a solution:
public abstract class AbstractGenericClass<TWrapper<T>, T> where TWrapper : AbstractGenericWrapper<T> {
protected TWrapper tWrapper;
//our SomeEnum se is somehow relatable to every T
//but we don't want to know how
protected SomeEnum se = ...;
//called on start
public abstract void Start();
}
public abstract class AbstractGenericWrapper<T> {
T _t;
public AbstractGenericWrapper(T t) {
_t = t;
}
}
public class ConcreteNongenericClass : AbstractGenericClass<VendorSpecificImplementation> {
//called on start
public override void Start() {
List<VendorSpecificImplementation> ts = FindObjectsOfType<VendorSpecificImplementation>;
foreach (VendorSpecificImplementation t in ts) {
if(t.vendorVariable.toLower().Equals(Enum.GetValues(typeof(someEnum), someEnum))) {
tWrapper = new ConcreteNongenericWrapper(t);
}
}
}
}
public class ConcreteNongenericWrapper : AbstractGenericWrapper<VendorSpecificImplementation> {
}
public class OtherConcreteNongenericClass : AbstractGenericClass<OtherVendorSpecificImplementation> {
//called on start
public void Start() {
List<OtherVendorSpecificImplementation> ts = FindObjectsOfType<OtherVendorSpecificImplementation>;
foreach (OtherVendorSpecificImplementation t in ts) {
if(t.otherVendorVariable % 3 == (int) someEnum) {
tWrapper = new OtherConcreteNongenericWrapper(t);
}
}
}
}
public class OtherConcreteNongenericWrapper : AbstractGenericWrapper<OtherVendorSpecificImplementation> {
}
public class SomeImplementation {
public static void main() {
AbstractGenericClass<ConcreteNongenericWrapper<ConcreteNongeneric>, ConcreteNongeneric> foo
= new AbstractGenericClass<ConcreteNongenericWrapper<ConcreteNongeneric>, ConcreteNongeneric>();
AbstractGenericClass<OtherConcreteNongenericWrapper<OtherConcreteNongeneric>, OtherConcreteNongeneric> bar
= new AbstractGenericClass<OtherConcreteNongenericWrapper<OtherConcreteNongeneric>, OtherConcreteNongeneric>();
foo.Start();
bar.Start();
}
}
Thank you very much for your time and help!

Forming this Generic-Inside-Nongeneric-Interface Call

public interface IMyClassClient
{
MyClass<T> Default();
//T cannot be found, error
}
public class MyClass<T>
{
private IMyClassClient owner;
private MyClass<T> test;
public MyClass<T> Test
{
get { return _test;}
set
{
if(value ==owner.Default())
{
//how to form this call?^^^^^
MessageBox.Show("Well that's true");
}
}
}
}
The objective is that a class implementing IMyClassClient will handle figuring out a return value for IMyClassClient.Default()
What am I missing here?
EDIT:
After some thought, the following is a better wording of what I need to accomplish:
public interface IMyInterface
{
//see Instance member in SomeClass, down below? How to declare it here?
}
public class SomeClass:IMyInterface
{
public MyClass<int> Instance()
{
return new myClass<int>(); //for brevity
}
}
You should use generic in interface too, if you don't do it, you couldn't write MyClass<T> Default() , you don'T have any T there.
public interface IMyClassClient<T>
{
MyClass<T> Default();
}
public class MyClass<T>
{
private IMyClassClient<T> owner;
private MyClass<T> test;
public MyClass<T> Test
{
get { return test; }
set
{
if (value == owner.Default())
{
MessageBox.Show("Well that's true");
}
}
}
}
EDIT: Sure, you could declare geeric function in non generic interface
public interface IMyInterface
{
MyClass<T> Instance<T>();
}
public class SomeClass : IMyInterface
{
public MyClass<int> Instance()
{
return new myClass<int>();
}
}

C# Is Not Assignable Type - Generics

So I'm just hacking around with a state machine type I was working on and mostly wanting to just try out the Activator.CreateInstance method to see what it was like, and I ran into a problem where I cant seem to use the where clause as I would think. I apologize ahead of time if I am just an idiot and everyone laughs me out of here. So I have 2 small classes.
public class TransitionContainer<TTransition, TStateTo> :
ITransitionContainer<TTransition, TStateTo>
where TTransition : ITransition
where TStateTo : IState
{
public TransitionContainer()
{
StateTo = typeof(TStateTo);
Transition = Activator.CreateInstance<TTransition>();
}
public Type StateTo { get; private set; }
public TTransition Transition { get; private set; }
}
as well as
public class StateContainer<T> : IStateContainer<T> where T : IState
{
private Dictionary<Type, TransitionContainer<ITransition, IState>> _transitions =
new Dictionary<Type, TransitionContainer<ITransition, IState>>();
public StateContainer()
{
State = Activator.CreateInstance<T>();
}
public T State { get; private set; }
public int TransitionCount
{
get { return _transitions.Count; }
}
public void AddTransition<TTransition, TStateTo>() where TTransition : ITransition, new()
where TStateTo : IState, new()
{
var transitionContainer= new TransitionContainer<TTransition, TStateTo>();
_transitions.Add(typeof(TTransition), transitionContainer);
}
So on the line _transitions.Add(typeof(TTransition), transitionContainer); I receive a cannot convert TransitionContainer<TTransition,TStateTo> expression to type TransitionContainer<ITransition,IState> error.
If I change the generic parameters to
var transitionContainer= new TransitionContainer<ITransition, IState>();
it works fine, but I wanted to use inherited types that are new() so I could be sure I could instantiate them.
Again I apologize if I'm doing something incredibly wrong, I was just kind of ran into a brick wall and my googling led me in no good direction. I didnt include any of the other interfaces or classes as they didn't seem to be part of the problem, but if there needed I can attach them. Thanks for any help!
This issue happens because:
ITransitionContainer is not a covariant interface over its type arguments.
AddTransition method generic arguments are not constrained to be reference types.
_transitions is not a dictionary with ITransitionContainer values, so without changing it to Dictionary<Type, ITransitionContainer<ITransition, IState>> we still won't be able to add even properly resticted covariant transtions.
Simplified example
Consider the following simplified case:
public interface ITransition
{
}
public class SomeTransition : ITransition
{
}
public interface ITest<TTransition>
where TTransition : ITransition
{
TTransition Value { get; }
}
public class SomeTest<TTransition> : ITest<TTransition>
where TTransition : ITransition
{
public TTransition Value
{
get
{
throw new NotImplementedException();
}
}
}
It will fail in both
public static void Do<TTransition>()
where TTransition : ITransition
{
ITest<ITransition> item = new SomeTest<TTransition>();
}
and
ITest<ITransition> item = new SomeTest<SomeTransition>();
If you make ITest covariant
public interface ITest<out TTransition>
, then it will fail only in generic method. Because here TTransition can be a struct and co/(contra)variance doesn't work with value types:
public static void Do<TTransition>()
where TTransition : ITransition
{
ITest<ITransition> item = new SomeTest<TTransition>();
}
But if you make that method constrained to only reference types, then it will work in both cases:
public static void Do<TTransition>()
where TTransition : class, ITransition
{
ITest<ITransition> item = new SomeTest<TTransition>();
}
Apply the same principle(out and class) to your two generic arguments and it will do the job.
Full solution for your specific case:
public interface IState
{ }
public interface ITransition
{ }
// !!!!! - Here we add out specifier
public interface ITransitionContainer<out TTransition, out TStateTo>
where TTransition : ITransition
where TStateTo : IState
{
Type StateTo
{
get;
}
TTransition Transition
{
get;
}
}
public interface IStateContainer<T> where T : IState
{
T State
{
get;
}
}
public class TransitionContainer<TTransition, TStateTo> : ITransitionContainer<TTransition, TStateTo>
where TTransition : ITransition
where TStateTo : IState
{
public TransitionContainer()
{
StateTo = typeof(TStateTo);
Transition = Activator.CreateInstance<TTransition>();
}
public Type StateTo { get; private set; }
public TTransition Transition { get; private set; }
}
public class StateContainer<T> : IStateContainer<T> where T : IState
{
private Dictionary<Type, ITransitionContainer<ITransition, IState>> _transitions =
new Dictionary<Type, ITransitionContainer<ITransition, IState>>();
public StateContainer()
{
State = Activator.CreateInstance<T>();
}
public T State { get; private set; }
public int TransitionCount
{
get { return _transitions.Count; }
}
public void AddTransition<TTransition, TStateTo>()
// !!!!!! - Here we add class constraints
where TTransition : class, ITransition, new()
where TStateTo : class, IState, new()
{
var transitionContainer = new TransitionContainer<TTransition, TStateTo>();
_transitions.Add(typeof(TTransition), transitionContainer);
}
}
That fails because generics are not covariant. The problem can be seen here:
TransitionContainer<ITransition, IState> value = new TransitionContainer<TTransition, TStateTo>();
That gives you the same error. You also get this error with something as simple as:
List<IComparable> list = new List<DateTime>();
Visual Studio tells you (basically) that:
Cannot implicitly convert type 'List<System.DateTime>' to 'List<System.IComparable>'
What you need to do is convert the object. You could create a Convert method that returns a TransitionContainer<ITransition, IState> and then use .Add(typeof(TTransition), transitionContainer.Convert()) (or whatever you name it).
But the most painless option is to create an implicit conversion for your TransitionContainer<TTransition, TStateTo> object by adding this static method:
public static implicit operator TransitionContainer<ITransition, IState>(TransitionContainer<TTransition, TStateTo> value)
{
return new TransitionContainer<ITransition, IState>() { StateTo = value.StateTo, Transition = value.Transition };
}
And that's it. :)
Of course, you will have to copy everything needed for it to work, in this case it seems these two objects are enough.

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

Categories