I have an issue with generic interface. The compiler does not give any compiling errors but at run-time unseen exception is thrown.
public interface IStructure
{
string Name {get;}
}
public interface IStructureNavigation<T> : IStructure where T : IStructure
{
T Parrent {get;}
}
public class ResourceStructure : IStructureNavigation<ResourceStructure>
{
private ResourceStructure _parrent;
public virtual string Name
{
get;
set;
}
public virtual ResourceStructure Parrent
{
get { return _parrent; }
}
}
Can someone explain why does the following code fail at runtime?
public class Action
{
private ObjectContext _context;
private ObjectSet<ResourceStructure> _structue;
private IQueryable<ResourceStructure > _parrents;
public Action()
{
string connectionString =
ConfigurationManager
.ConnectionStrings["Structure"].ConnectionString;
_context = new ObjectContext(connectionString);
_context.ContextOptions.LazyLoadingEnabled = true;
_structue = _context.CreateObjectSet<ResourceStructure>();
_parrents = _structue.Where(x => x.ParentID == null);
// FAILS IN FOREACH LOOP : UNSEEN EXCPTION
foreach (IStructureNavigation<IStructure> strt in _parrents)
{
//do something
}
//WORKS IF USING CONCRETE TYPE NOT INTERFACE
foreach(IStructureNavigation<ResourceStructure > strt in _parrents)
{
//do something
}
}
}
Declare T as covariant
public interface IStructureNavigation<out T> : IStructure where T : IStructure
That's because your instance is of type IStructureNavigator<ResourceStructure> and not IStructureNavigator<IStructure>.
If you need to use the interface, you can use the Cast extension method:
_parrents = _context.CreateObjectSet<ResourceStructure>().Cast<IStructure>();
Which version of the Framework are you using?
Related
Trying to create a factory to return a generic interface (following this answer) but getting the error:
Can't implicitly convert IFinancialsSyncService<Vendor, QuickBooksVendor> to IFinancialsSyncService<TEntity, TQuickBooksEntity>. Anexplicit conversion exists, are you missing a cast?
public class QuickBooksEntityServiceFactory
{
public IFinancialsSyncService<TEntity, TQuickBooksEntity> Create<TEntity, TQuickBooksEntity>()
where TEntity : class, IEntity, IFinancials, new()
where TQuickBooksEntity : class, IQuickBooksEntity
{
if (typeof(TEntity) == typeof(QuickBooksVendor))
{
return new QuickbooksVendorService();
}
throw new InvalidOperationException();
}
}
The service confirms to the IFinancialsSyncService interface:
public class QuickbooksVendorService : IFinancialsSyncService<Vendor, QuickBooksVendor>
However, if I cast it explicitly, I get a Cast is redundant error along with the first error still.
return (IFinancialsSyncService<Vendor, QuickBooksVendor>)new QuickbooksVendorService();
So the error is confusing me. What am I doing wrong?
UPDATE
This is what I'm trying to simplify. There are several instances similar to this also that call other common methods of the interface.
switch (enumDataElement)
{
//Export jobs
case DataElement.Item:
var itemService = new QuickbooksItemService();
exportResult = itemService.UpdateMozzoEntityWithFinancialsId(session, response, EntityId, intUserId);
break;
case DataElement.Vendor:
var VendorService = new QuickbooksVendorService();
exportResult = UpdateMozzoEntityWithFinancialsId(new QuickbooksVendorService(),session, response, EntityId, intUserId);
break;
case DataElement.Bill:
var billService = new QuickbooksBillService();
exportResult = billService.UpdateMozzoEntityWithFinancialsId(session, response, intUserId);
break;
case DataElement.PurchaseOrder:
var qbPOService = new QuickbooksPurchaseOrderService();
exportResult = qbPOService.UpdateMozzoEntityWithFinancialsId(session, response, intUserId);
break;
case DataElement.SalesReceipt:
var salesReceiptService = new QuickbooksSalesReceiptService();
exportResult = salesReceiptService.UpdateStratusEntityWithFinancialsId(session, response, intUserId);
break;
}
And replace it with something like:
var qbEntityService = EntityServiceFactory.Create(enumDataElement);
What would this factory look like?
This has to do with Liskov's Substitution Principle. Imagine that your Generic type is instead a property of the interface:
public interface IFinancials { }
public interface IFinancialsSyncService
{
IFinancials Financials { get; set; }
}
Now we implement this interfaces:
public class Financials : IFinancials {}
public class FinancialsSyncService : IFinancialSyncService
{
public Financials Financials { get; set; }
}
This results in a compiler error:
Compilation error: 'Program.FinancialsSyncService' does not implement interface member 'Program.IFinancialsSyncService.Financials'. 'Program.FinancialsSyncService.Financials' cannot implement 'Program.IFinancialsSyncService.Financials' because it does not have the matching return type of 'Program.IFinancials'.
Both problems have the same issue. In my example, the interface states that the result is of type IFinancials but is a more specific derived type Financials and even though any valid value that is placed in the property fulfills the interface, it cannot be replaced with any value derived from IFinancials only types that derive from Financials.
So if your code looked like:
public interface IFinancialsSyncService<TEntity>
where TEntity : IEntity
{
TEntity Financials { get; set; }
}
and you create a class:
public class QuickbooksVendorService : IFinancialSyncService<Vendor>
{
public Vendor Financials { get; set; }
}
However, now QuickbooksVendorService is a IFinancialSyncService<Vendor> not a IFinancialSyncService<TEntity> because the property is the derived type. Even if you didn't have this specific property it still leads to the same problem that generic type is more specific than the interface.
use Factory method and Adapter pattern
[TestFixture]
public class Class1
{
[Test]
public void Go()
{
var qbItem = Export(1);
var qbVendor= Export(2);
var qbSales= Export(3);
}
public qbEntityService Export(int number)
{
var qb = Class1.Create(number);
return qb.UpdateMozzoEntityWithFinancialsId();
}
public static IEntityService Create(int enumDataElement)
{
switch (enumDataElement)
{
case 1:
return new QuickbooksItemService();
case 2:
return new QuickbooksVendorService();
case 3:
return new QuickbooksSalesReceiptServiceAdapter(new QuickbooksSalesReceiptService());
default:
throw new Exception();
}
}
}
public interface IEntityService
{
qbEntityService UpdateMozzoEntityWithFinancialsId();
}
public class qbEntityService
{
}
public class QuickbooksItemService : IEntityService
{
public qbEntityService UpdateMozzoEntityWithFinancialsId()
{
Console.WriteLine("I am QuickbooksItemService, performing UpdateMozzoEntityWithFinancialsId");
return new qbEntityService();
}
}
public class QuickbooksVendorService : IEntityService
{
public qbEntityService UpdateMozzoEntityWithFinancialsId()
{
Console.WriteLine("I am QuickbooksVendorService, performing UpdateMozzoEntityWithFinancialsId");
return new qbEntityService();
}
}
public class QuickbooksSalesReceiptService
{
public qbEntityService UpdateStratusEntityWithFinancialsId()
{
Console.WriteLine("I am QuickbooksSalesReceiptService, performing UpdateStratusEntityWithFinancialsId");
return new qbEntityService();
}
}
public class QuickbooksSalesReceiptServiceAdapter : IEntityService
{
private QuickbooksSalesReceiptService adaptee;
public QuickbooksSalesReceiptServiceAdapter(QuickbooksSalesReceiptService adaptee)
{
this.adaptee = adaptee;
}
public qbEntityService UpdateMozzoEntityWithFinancialsId()
{
return adaptee.UpdateStratusEntityWithFinancialsId();
}
}
I have IInfo and its generic version:
public interface IInfo
{
IInput Input { get; }
}
public interface IInfo<T> : IInfo where T : IInput
{
new T Input { get; }
}
Class implementation:
public class Info : IInfo<IInput>
{
public IInput Input { get; }
public Info (IInput input) {}
}
Factory to create IOutput from IInput:
public class GenericFactory<TInput, TOutput> where TInput : IInput where TOutput : IOutput
{
public IOutput Create(IInfo info)
{
ConstructorInfo cInfo = typeof(TOutput).GetConstructor(new[] { typeof(IInfo<TInput>) });
object output = cInfo.Invoke(new object[] {cInfo});
}
}
To test the above code:
public class TestInput : IInput
{
}
public abstract class AbstractOutput<TInput> : IOutput where TInput : IInput
{
}
public class TestOutput: AbstractOutput<TestInput>
{
public TestOutput(IInfo<TestInput> info)
{
}
}
public void Test()
{
IInput input = new TestInput();
IInfo info = new Info(input);
var factory = new GenericFactory<TestInput, TestOutput>();
IOutput output = factory.Create(info);
}
I get the following error:
Object of type 'Info' cannot be converted to type'Info<TestInput>'.
Side note: I'm open to any suggestions to simplify/re-write the code in a different way.
public TestOutput(IInfo<TestInput> info)
{
}
Is expecting an IInfo<TestInput> explicitly. However, you're trying to call it with IInfo<IInput> (which is what Info is designed to be).
To make it clear, you could also write:
IInput input = new OtherInput();
IInfo info = new Info(input);
var factory = new GenericFactory<TestInput, TestOutput>();
IOutput output = factory.Create(info);
And now you've provided IInfo<OtherInput> to something expecting IInfo<TestInput>
You would need to make IInfo<T> contravariant to allow it to be cast, for example:
public interface IInfo<in T> : IInfo
where T : IInput
{
//new T Input { get; }
}
But note that it's illegal to return T when with a contravariant interface. The alternative is to make Info generic, and change Create to accept IInfo<TInput>. That latter gives you the benefit of a compile-time error when trying to pass IInfo<OtherInput> to Create(), rather than a run-time error
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.
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
Am I doing something wrong or is it not possible to specify a generic class as a constraint to a generic method?
I have been playing around with generics and db4o (open source object database) and am writing a test program (see code below) to store and retrieve some user defined generic collections.
I am attempting to write a generic method (see GetCollectionFromDb below) to retrieve a specifically typed collection from the database. Unfortunately, the code below returns a compiler generated error for the line:
MyCollection1 collection3 =
GetCollectionFromDb<MyCollection1>(Collection1Name);
The error message is:
The type 'GenericsTest.MyCollection1'cannot be used as type parameter 'T'
in the generic type or method 'GenericsTest.Program.GetCollectionFromDb<T>(string)'.
There is no implicit reference conversion from'GenericsTest.MyCollection1' to
'GenericsTest.MyCollectionBase<GenericsTest.MyCollection1>'.
I would appreciate any suggestions as to what I may be doing wrong or how I could approach this differently to reach the deisred outcome.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Db4objects.Db4o;
namespace GenericsTest
{
public class Entity1
{
public string SomeProperty { get; set; }
}
public class Entity2
{
public string SomeProperty { get; set; }
}
public abstract class MyCollectionBase<T> : Collection<T>
{
protected MyCollectionBase() : this("") { }
protected MyCollectionBase(string pCollectionName)
{
CollectionName = pCollectionName;
}
public string CollectionName { get; set; }
}
public class MyCollection1 : MyCollectionBase<Entity1>
{
public MyCollection1(string pCollectionName) :
base(pCollectionName) { }
public void DoSomeWorkOnCollection1() {}
}
public class MyCollection2 : MyCollectionBase<Entity2>
{
public MyCollection2(string pCollectionName) :
base(pCollectionName) { }
public void DoSomeWorkOnCollection2() { }
}
public class Program
{
public static IObjectContainer db = null;
public static void Main(string[] args)
{
const string Collection1Name = "Entity1Collection";
const string Collection2Name = "Entity2Collection";
db = Db4oFactory.OpenFile("Test.db");
Entity1 entity1 = new Entity1();
MyCollection1 collection1 = new MyCollection1(Collection1Name);
collection1.Add(entity1);
db.Store(collection1);
Entity2 entity2 = new Entity2();
MyCollection2 collection2 = new MyCollection2(Collection2Name);
collection1.Add(entity1);
db.Store(collection2);
db.Commit();
db.Close();
db = Db4oFactory.OpenFile("Test.db");
MyCollection1 collection3 =
GetCollectionFromDb<MyCollection1>(Collection1Name);
}
private static T GetCollectionFromDb<T>(string pCollectionName)
where T : MyCollectionBase<T>
{
IList<T> queryResult = db.Query((T c) =>
c.CollectionName == pCollectionName);
if (queryResult.Count != 0) return queryResult[0];
return null;
}
}
}
Your type doesn't satisfy the constraint. You've supplied MyCollection1 which derives from MyCollection<Entity1>. However, that doesn't mean it derives from MyCollection<MyCollection1>.
Perhaps you want to express the constraint over two type parameters instead of one:
private static T GetCollectionFromDb<T, U>(string pCollectionName)
where T : MyCollectionBase<U>
Then call it with:
GetCollectionFromDb<MyCollection1, Entity1>(Collection1Name);
If that doesn't do the trick, please let us know why.
Just follow the T:
// ...
{
//...
MyCollection1 collection3 = GetCollectionFromDb<MyCollection1>(Collection1Name);
}
private static T GetCollectionFromDb<T>(string pCollectionName) where T : MyCollectionBase<T>
{
IList<T> queryResult = db.Query((T c) => c.CollectionName == pCollectionName);
if (queryResult.Count != 0) return queryResult[0];
return null;
}
would translate into:
private static MyCollection1 GetCollectionFromDb<MyCollection1>(string pCollectionName) where T : MyCollectionBase< MyCollection1 >
{
IList< MyCollection1 > queryResult = db.Query((MyCollection1 c) => c.CollectionName == pCollectionName);
if (queryResult.Count != 0) return queryResult[0];
return null;
}
Which is not what you want since MyCollection1 derives off MyCollectionBase< Entity1 > and not MyCollectionBase< MyCollection1 >, which is why you got the error. If you want the constraint to work, you will probably have to use a second type identifier to express the type being used in the generic collection.