Trying to generate a generic queue of filters to be applied to an image (where in the example filter it uses OpenCVSharp.GaussianBlur, but making it generic so I can plugin any custom filter I create).
I am struggling a bit with the C# generics and intellisense is showing:
cannot convert from 'GaussianBlur' to 'IFilter
Intellisense is recommending changing the following line:
filters.Enqueue(filter);
by casting to the interface
filters.Enqueue((IFilter<IFilterParams>)filter);
however, my question is why is casting required when the concrete class implements the interface and requires it by the generics definitions or am I misunderstanding how to declare the classes using generics.
Current implementation code is as follows:
public class FilterTest
{
private FilterCollection filters = new FilterCollection();
/* ... other irrelevant code ... */
public void ApplyFilters(ref Mat buffer)
{
var filter = new GaussianBlur(new GaussianBlurParams { KernelSize = new Size(6, 6) });
filters.Enqueue((IFilter<IFilterParams>)filter);
filters.Apply(ref buffer);
}
}
.
I am extending the Queue<> class for the FilterCollection:
public class FilterCollection : Queue<IFilter<IFilterParams>>
{
public void Apply(ref Mat buffer)
{
while (Count > 0)
Dequeue().Apply(ref buffer);
}
}
and the interfaces for IFilter and IFilterParams are as follows:
public interface IFilter<T> where T : IFilterParams
{
void Apply(ref Mat buffer);
}
public interface IFilterParams { }
And then the sample filter implementation (more or less just a wrapper in this case):
public class GaussianBlurParams : IFilterParams
{
public Size KernelSize = new Size(5, 5);
public double SigmaX = default(double);
public double SigmaY = default(double);
public BorderTypes BorderType = BorderTypes.Default;
}
public class GaussianBlur : IFilter<GaussianBlurParams>
{
private GaussianBlurParams p;
public GaussianBlur(GaussianBlurParams filterParams)
{
this.p = filterParams;
}
public void Apply(ref Mat buffer)
{
Cv2.GaussianBlur(buffer, buffer, p.KernelSize, p.SigmaX, p.SigmaY, p.BorderType);
}
}
So given:
GaussianBlur implements IFilter<GaussianBlurParams>
and IFilter<T> where T : IFilterParams
and GaussianBlurParams implements IFilterParams
Is casting the only way to fix this or is there something wrong with the structure of the generic classes/interfaces as written?
There are multiple aspects to your code that get entangled here and that altogether make the design that less than optimal. At first glance this might look like an issue of covariance, but on a closer look this is not the case. The two main aspects here are generic constraints and interfaces. To understand what I mean, let's take a look at some of the benefits of these two language elements.
Generic constraints
While generics enable you to use an implementation of a pattern for multiple types in a type-safe way, there is not much you can do to directly manipulate an object of type T from within the generic class. You can not create an instance, you can not rely on instances being reference or value types (try a comparison with null to see what that means) and you can not access any other members than the ones defined in System.Object. That's why you can use generic constraints to allow code within the generic class that can do additional things with objects of type T, like creating instances (with the new() constraint) or access additional members (by constraining T to a certain type and/or one or more interfaces).
Interfaces
Interfaces provide a contractual guarantee that the implementer will have a defined set of members. This guarantee is directed to consumers of the interface, not to its implementers. That means that you don't use interfaces to force its implementer to provide some members that are not of any value to the consumer of the interface.
What this means in your case
The key to your problems is this part of your code:
public interface IFilter<T> where T : IFilterParams
{
void Apply(ref Mat buffer);
}
public interface IFilterParams { }
In particular:
You define the generic constraint where T : IFilterParams, but IFilterParams provides no members. This constraint adds no value to your design. You restrict the implementer to a certain T, but you don't gain anything from it, because you can't do anything with instances of T that you couldn't do without the constraint.
Going one step further, you don't need the interface to be generic at all. You don't even use T in the only member that the interface provides. You could do just fine without it as far as the guarantees of the interface are concerned.
Taking a look at the GaussianBlur implementation of IFilter<T>, it is clear that you use the GaussianBlurParams only in the constructor, which is not part of the interface. So you are using the constraint of the interface only to restrict the implementer to use a Params class that implements IFilterParams. That is not even a real restriction, because the implementer can use any other parameter class for its initialization. But mainly this violates the principle that the interface provides a guarantee to its consumers, not a restriction its implementers.
Putting this all together, you can simply go for...
public interface IFilter
{
void Apply(ref Mat buffer);
}
...and you have avoided all the problems you are facing.
Even if you would need T with the constraint where T : IFilterParams for another consumer of the interface (maybe there is another interface member that you have not added in your example), your FilterCollection does not need this constraint. So you can still keep a non-generic IFilter and provide another interface (that might or might not inherit from IFilter) that provides the additional capabilities.
Ok, thanks to #zzxyz's original comment along with the comments and answer that were added and then shortly after removed, lead me to research more on covariance (which I created by adding the generic IFilterParams in order to avoid covariance), and answers/comments in SO (Contravariance? Covariance? What's wrong with this generic architecture...?) helped me correct my problem and better structure the code.
Now I understand how I was trying to 'add a banana to a bowl(fruit) (fruit being the covariant as it is not just one 'type' of fruit) when I needed to be adding the banana to a bowl(bananas)'. I am understanding but quite poorly summing up one of the answers that were unfortunately deleted.
In researching, I was able to remove the covariance by creating an abstract class with its own generic type for the filterParams and removing the IFilterParams interface altogether, thus all filters must implement the base abstract class, and now no longer cause covariance.
Since I understand it now, but not well enough to explain (above) clearly, the revised code (below) may help explain better.
First, no changes to the FilterTest class were necessary (except removing the casting from the original example which was the point of the question):
public class FilterTest
{
private FilterCollection filters = new FilterCollection();
public void ApplyFilters(ref Mat buffer)
{
var filter = new GaussianBlur(new GaussianBlurParams { KernelSize = new Size(6, 6) });
filters.Enqueue(filter);
filters.Apply(ref buffer);
}
}
Next, adjusted the Queue so it was not covariant (implements one 'type' IFilter) which exposes the needed 'Apply' method.
public class FilterCollection : Queue<IFilter>
{
public void Apply(ref Mat buffer)
{
while (Count > 0)
Dequeue().Apply(ref buffer);
}
}
public interface IFilter
{
void Apply(ref Mat buffer);
}
And finally removed the IFilterParams as they are no longer relevant to the cause. Now the sample filter implementation looks like:
public class GaussianBlur : IFilter
{
private GaussianBlurParams p;
public GaussianBlur(GaussianBlurParams filterParams)
: base(filterParams)
{
}
public override void Apply(ref Mat buffer)
{
Cv2.GaussianBlur(buffer, buffer, p.KernelSize, p.SigmaX, p.SigmaY, p.BorderType);
}
}
public class GaussianBlurParams
{
public Size KernelSize = new Size(5, 5);
public double SigmaX = default(double);
public double SigmaY = default(double);
public BorderTypes BorderType = BorderTypes.Default;
}
Problem fixed, hopefully helps others!
Related
Is it possible to declare a generic collection to hold only objects implementing a generic Interface with any <T>?
My question burns down to: If I want to/have to store objects implementing a generic interface, is there a better way to express that fact than using a non generic collection or (generic of <Object>).
Example:
// An example Generic Interface
interface ISyncInterface<T>
{
Task DoSync();
IEnumerable<T> NewItems { get; }
}
// a manager-class that registers different classes implementing
// the generic interface.
// The code works - can it be done better?
class Manager
{
private List<Object> _services = new List<Object>(); // <- works but is basically non generic
// however the RegisterService() ensures that only correct types can be added.
// would like to have something like below to indicate the Interface-Type
// however: this would only allow _services2.Add to hold types of ISyncInterface<Object>
// - ISyncInterface<ServiceA_DTO> would fail.
private List<ISyncInterface<Object>> _services2 = new List<ISyncInterface<Object>>();
void RegisterService<T, U>(T service)
where T : ISyncInterface<U>
{
_services.Add(service); // <- works e.g. for SyncServiceA
// _services2.Add(service); // <- FAILS for SyncServiceA - no conversion
// _services2.Add((ISyncInterface<Object>) service); // <- FAILS also - no explicit cast
}
}
// SETUP - The classes used above. Just to clarify.
class ServiceA_DTO { }
class ServiceB_DTO { }
class SyncServiceA : ISyncInterface<ServiceA_DTO>
{
public Task DoSync() {}
public IEnumerable<ServiceA_DTO> NewItems { get; }
}
class SyncServiceB : ISyncInterface<ServiceB_DTO>
{
public Task DoSync() {}
public IEnumerable<ServiceB_DTO> NewItems { get; }
}
Is this possible at all? Any advice is highly appreciated!
Update: New, more verbose code to clarify the problem.
Below there was a suggestion to base the generic interface on an non generic one. But as a consequence all implementing classes of the generic interface would have to implement the non generic methods, properties etc. - or is there a way around it?
Thanks for your input!
Is it possible to declare a generic collection to hold only objects implementing a generic interface instantiated with any T?
Short answer: no.
Longer answer: no, because that is not useful.
Let's consider a simple generic interface:
interface I<T> { T Get(); }
And a bunch of objects that implement it:
class Lion : I<Lion>
{
public Lion Get() => this;
}
class TaxPolicyFactory : I<TaxPolicy>
{
public TaxPolicy Get() => new TaxPolicy();
}
class Door: I<Doorknob>
{
public Doorknob Get() => this.doorknob;
...
}
OK, now suppose you have a List<I<ANYTHING>> like you want:
var list = new List<I<???>> { new TaxPolicyFactory(), new Lion(), new Door() };
You've got a list with a tax policy factory, a lion and a door in it. Those types have nothing in common with each other; there's no operation you can perform on each of those objects. Even if you could call Get on each of them, then you'd have a sequence with a tax policy, a lion and a doorknob in it, and what are you going to do with that?
Nothing, that's what. The constraint "implements interface I<T> for any T" is simply not a useful constraint in C#, so there is no way to express it.
It sounds like you have an "XY" problem. That is a problem where you have a bad solution in mind, and now you are asking questions about your bad solution. Ask us a question about the real problem you have, not the bad idea you've got for its solution. What's the real problem?
UPDATE: With the new information in the question it is now much more clear. The feature you want is called generic interface covariance, which was my favourite feature for C# 4.
If you update your interface definition to
interface ISyncInterface<out T> { ... }
then you can use an ISyncInterface<String> in a context where an ISyncInterface<Object> is expected. For example, you could put an ISyncInterface<Giraffe> into a List<ISyncInterface<Animal>> or whatever.
However you are required to ensure that your interface definition only uses T in a covariantly valid position. Your interface is valid as stated, but if for example you ever want to add a method void M(T t); to your interface, it will no longer be covariantly valid. The "out" is a mnemonic telling you that T can only be used as output of methods. Since IEnumerable<T> is also covariantly valid, it's fine; there are no inputs of T in an IEnumerable<T>.
Also, variance only works with generic interfaces and delegates, and the varying types must be reference types. You can't put an ISyncInterface<int> into a List<ISyncInterface<Object>> because int is not a reference type.
There are many posts on SO about covariance and contravariance; you should also read the Microsoft documentation. It can be a confusing feature. If you're interested in the historical details of how we designed and implemented the feature, see my blog.
Perhaps you can try something like this:
public interface MyInterface
{//methods common to all types
void FirstMethod();
}
public interface MyInterface<T> : MyInterface
{//methods specific to a type
void FirstMethod(T parameter);
}
public class MyClassThatHandlesAllInterfaces
{
private List<MyInterface> _allInterfacesT; //first interface in the chain
public void AddInterface<T>(MyInterface<T> ifToAdd)
{
_allInterfacesT.Add(ifToAdd); // <- this is what I'd like to do
}
}
I use this pattern quite often. Because I do not know all the details about your scenario it might not be suitable for you.
But it might help other people searching google.
If we have two interfaces with a common base that a class must implement, is it possible to explicitly implement the common base?
Consider IEnumerable<T> for example:
public class MyMultiEnumerable: IEnumerable<int>, IEnumerable<double>
{
private readonly List<int> intList = new List<int>();
private readonly List<double> doubleList = new List<double>();
IEnumerator<double> IEnumerable<double>.GetEnumerator()
{
return doubleList.GetEnumerator();
}
IEnumerator<int> IEnumerable<int>.GetEnumerator()
{
return intList.GetEnumerator();
}
public IEnumerator GetEnumerator()
{
/*
* How do we deal with this common case, where in context it means
* two different things?
*/
throw new System.NotImplementedException();
}
}
I have no requirement to do so, but I am interested from a theoretical perspective.
Update #1
I think my use of IEnumerable and basic types is conflating this, and after thinking about it some more, I believe it could be stripped back to this problem:
public interface CommonBase
{
void CommonMethod();
}
public interface AltBase1 : CommonBase
{
void UnommonMethod();
}
public interface AltBase2 : CommonBase
{
void UnommonMethod();
}
public class Example : AltBase1, AltBase2
{
public void CommonMethod()
{
/*
* If this method needs to know whether it is being called on behalf
* of AltBase1 or AltBase2, how could it determine the implementation?
*/
}
void AltBase2.UnommonMethod()
{
}
void AltBase1.UnommonMethod()
{
}
}
Because it doesn't appear possible to explicitly implement an interface's inherited members (Example of how this might look below). I strongly believe this isn't possible via any conventional means, as it appears to be a multiple inheritance problem.
void AltBase1.CommonBase.CommonMethod()
{
// AltBase1 targeted Implementation
}
Could it be achieved via any Meta-based approach, or not at all?
I'm not sure if it's possible, but I think it's a code smell. The only scenario where this is viable is as you've phrased it - when implementing a specific version for a generic class. In that scenario, you should be leveraging the type variables, rather than how you've got it right now.
So you can get it to a point where you can compile your code. From the IEnumerable interface you have to implement GetEnumerator(), but you have to decide if you want to return intList or doubleList. Both IEnumerable<int> and IEnumerable<double> have the same signature on GetEnumerator() so there's no way to implement them both since the compiler will complain.
Take a look at this post as it has good compatible/incompatible examples of how to deal with interfaces with the same method.
Implementing two interfaces in a class with same method. Which interface method is overridden?
Context: .NET 4.0, C#
I'm creating a set of interfaces and a set of clases that implement them to provide some service. The clients use the concrete clases but call methods that are declared using the interfaces as parameter types.
A simplified example is this one:
namespace TestGenerics
{
// Interface, of fields
interface IField
{
}
// Interface: Forms (contains fields)
interface IForm<T> where T : IField
{
}
// CONCRETE CLASES
class Field : IField
{
}
class Form <T> : IForm<T> where T : IField
{
}
// TEST PROGRAM
class Program
{
// THIS IS THE SIGNATURE OF THE METHOD I WANT TO CALL
// parameters are causing the error.
public static void TestMethod(IForm<IField> form)
{
int i = 1;
i = i * 5;
}
static void Main(string[] args)
{
Form<Field> b = new Form<Field>();
Program.TestMethod(b);
}
}
}
The code makes sense to me, but I get the compiler error:
Argument 1:
cannot convert from 'TestGenerics.Form<TestGenerics.Field>' to
'TestGenerics.IForm<TestGenerics.IField>' TestGenerics
I'm not sure what I'm doing wrong, I've read lots of pages on the internet but none solved my problem.
Is there a solution that would not modify that much the architecture of what I'm trying to build:
Edit:I designed the interfaces in a way such that they should be independent of concrete clases that implement them. The concrete clases could be loaded from a dll, but most of the application Works with the interfaces. In some cases I need to use concrete clases, specially when using clases that need to be serialized.
Thanks in advance.
Alejandro
The problem is that Form<Field> implements IForm<Field> but not IForm<IField>. You cannot use an inherited class (or interface) as a generic parameter unless it is marked as covariant with the out identifier. However, marking your interface as covariant will restrict the usage significantly (basically making in an "output-only" interface like IEnumerable) so it may not work for you.
One way to get it to work is to make TestMethod generic as well:
public static void TestMethod<T>(IForm<T> form) where T:IField
{
int i = 1;
i = i * 5;
}
You can use Covariance, like so:
interface IForm<out T> where T : IField
{
}
More about Covariance and Contravariance here.
Others have pointed out the reasoning behind the error message, but let's examine the design of your sample code for a moment. Perhaps you're using a generic where none is needed.
You've already said you're using methods declared in the IField interface, so there may be no need to make your IForm class generic - simply have it store references to IField, instead of the generic argument 'T' (which is already guaranteed to be an IField anyway).
For instance, use:
public interface IForm
{
IEnumerable<IField> Fields { get; set; }
}
instead of
public interface IForm<T> where T : IField
{
IEnumerable<T> Fields { get; set; }
}
The following code shows what I would like to do; that is, I would like to constrain anObject, so that it can be used as a parameter to various methods with use IInterfaceOne or IInterfaceTwo, where neither inherits from the other.
public interface IInterfaceOne { }
public interface IInterfaceTwo { }
public class Implementation : IInterfaceOne, IInterfaceTwo
{
}
public interface IInterfaceOneAndTwo : IInterfaceOne, IInterfaceTwo { }
public class UsingImplementation
{
IInterfaceOneAndTwo anObject = (IInterfaceOneAndTwo)(new Implementation()); //fails because Implementation doesnt acctually implement IInterfaceOneAndTwo
}
This example fails however as IInterfaceOneAndTwo is an interface in its own right, and Implementation does not implement it.
I know if I used generics I could constrain them, but I am wondering, if there is a way to do this without generics?
Is there a way to say anObject shall implement IInterfaceOne and IInterfaceTwo, without using IInterfaceOneAndTwo?
Not the way you have it currently. Only generic constraints have that ability.
You could rewrite it to use generics:
public class UsingImplementation<T>
where T : IInterface1, IInterface2, new()
{
T anObject = new T();
void SomeMethod() {
anObject.MethodFromInterface1();
}
}
You can also have generic methods, not only generic classes
public void DoSomething<T>(T value)
where T : IInterface1, IInterface2
{
value.DoInterface1Things();
value.DoInterface2Things();
}
Or
public void DoSomething<T>()
where T : IInterface1, IInterface2, new()
{
T anObject = new T();
}
You can't do that in C# without generics but there is an alternative workaround to solve the problem without generics that was not mentioned here and might fit for you. This style is often used together with the IoC principle. You could inject the same object twice. Let me change your sample quite a bit...
public interface IInterfaceOne { void Hello(); }
public interface IInterfaceTwo { void World(); }
public class Implementation : IInterfaceOne, IInterfaceTwo
{
public void Hello() { };
public void World() { };
}
public class UsingImplementation
{
private readonly IInterfaceOne one;
private readonly IInterfaceTwo two;
public UsingImplentation(IInterfaceOne one, IInterfaceTwo two)
{
this.one = one;
this.two = two;
}
// do the stuff you want to do with an IInterfaceOne using field one
public DoSomeThingWithOne() { one.Hello(); }
// do the stuff you want to do with an IInterfaceTwo using field two
public DoSomeThingWithTwo() { two.World(); }
}
Then you could wire up the things this way:
var oneAndTwo = new Implementation();
var a = new UsingImplementation(oneAndTwo, oneAndTwo);
// operates on the first param (which is the same as the second)
a.DoSomeThingWithOne();
// operates on the second param (which is the same as the first)
a.DoSomeThingWithTwo();
Have a look for IoC principle (Inversion of Control) and Dependency Injection and you'll find more solutions similiar to this one.
This way you don't need to create an extra Interface combining InterfaceOne and InterfaceTwo, two.
"Incoming" generic class parameters and generic method parameters can combine types, but there is no facility for variables or fields to represent "composite" types. Further, in order to pass an object to a parameter of a generic type which combines multiple constraints, the object must be cast to a type which in fact implements all of those constraints. This can be difficult.
For example, suppose class Foo and Bar both implement Intf1 and Intf2. One wishes to write a function AddToList<T>(thing as T) where T:Intf1,Intf2. Such a function will perfectly happily accept objects of type Foo or Bar. Suppose, however, one wishes to use such a function to add all objects to the same list (which might be a mix of Foo, Bar, and any number of other types that also happen to implement Intf1 and Intf2) and then later pass those objects to a function whose parameter is likewise constrained to implement both Intf1 and Intf2. One could cast to Foo any object which happened to be a Foo, and cast to Bar any object which happened to be a Bar, but if other types are written which also handle Intf1 and Intf2, it would be difficult to deal with them.
It is possible to solve the problem, somewhat awkwardly, without using Reflection or other such tricks. Define an interface IActUpon<Base1, Base2> with a method ActUpon<thingType>ActUpon(thingType thing) where thingType: Base1, Base2. Implementations of such a method will be able to pass parameter thing to other methods requiring generic method parameter constrained to Base1 and Base2. The biggest difficulties with such an approach are that one must write separate code for each possible number of constraints, and that in many places where one would have used a lambda expression one will instead have to write an implementation of IActUpon....
If this is desirable then there has to be a logical connection between IInterfaceOne and IInterfaceTwo and the implementing class should implement the combined interface:
class Implementation : IInterfaceOneAndTwo { ... }
If this is not possible, because it's not (all) your code then you may have to rethink the UsingImplementation. It simply doesn't fit the available surface.
I'm trying to implement a strategy pattern to allow me to allow me to apply some "benefit" to an "account". In the code below, I can't add my implementation of an interface to a dictionary expecting the interface. I think it's some kind of contravariance problem, but it feels like I should be able to do this:
EDIT:
Since the answer seems to be that it's just not possible, any suggestions on how to achieve what I'm going for here?
void Main()
{
var provider = new BenefitStrategyProvider();
var freeBenefit = new FreeBenefit();
var strategy = provider.GetStrategy(freeBenefit);
strategy.ApplyBenefit(freeBenefit, new Account());
}
public class BenefitStrategyProvider
{
private Dictionary<Type, IBenefitStrategy<BenefitBase>> _strategies = new Dictionary<Type, IBenefitStrategy<BenefitBase>>();
public BenefitStrategyProvider()
{
/* Why can't I add this? */
_strategies.Add(typeof(FreeBenefit), new FreeBenefitStrategy());
}
public IBenefitStrategy<BenefitBase> GetStrategy(BenefitBase benefit)
{
return _strategies[benefit.GetType()];
}
}
public class Account {}
public abstract class BenefitBase
{
public string BenefitName {get;set;}
}
public class FreeBenefit : BenefitBase {}
public interface IBenefitStrategy<T> where T: BenefitBase
{
void ApplyBenefit(T benefit, Account account);
}
public class FreeBenefitStrategy : IBenefitStrategy<FreeBenefit>
{
public void ApplyBenefit(FreeBenefit benefit, Account account)
{
Console.WriteLine("Free Benefit applied");
}
}
EDITED - The formatting engine had removed everything in <angled brackets>, which made it rather impossible to understand. Sorry if this was confusing!
FreeBenefitStrategy implements IBenefitStrategy<FreeBenefit>. It can apply only FreeBenefits, not any other kind of benefit. It is not an IBenefitStrategy<BenefitBase>, so you can't put it in a collection of those. Logically, IBenefiteStrategy could be contravariant in BenefitBase, but this doesn't help you here - an IBenefitStrategy<BenefitBase> claims to be able to apply all kinds of benefits, so an IBenefitStrategy<BenefitBase> is-an IBenefitStrategy<FreeBenefit>, but the converse is not true - an IBenefitStrategy<FreeBenefit> cannot apply any BenefitBase.
I don't think there's any way to have a heterogenous collection like you want without using type-casting. If you think about it, there's no method that you can invoke on both an IBenefitStrategy<FreeBenefit> and an IBenefitStrategy<ExpensiveBenefit> beyond those that they share from object, so it makes sense that a variable of type object is the only thing that can point to either. If you want to keep them in the same dictionary, you'll need to make it a Dictionary<Type, object>. You could change GetStrategy to be generic and apply appropriate type-casting, but do be careful when looking up your dictionary - think what will happen if the object passed in is of a sub-class of FreeBenefit.
See: Covariance and Contravariance FAQ
How can I create variant generic interfaces and delegates myself?
The out keyword marks a type parameter as covariant, and the in keyword marks it as contravariant. The two most important rules to remember:
You can mark a generic type parameter as covariant if it is used only as a method return type and is not used as a type of formal method parameters.
And vice versa, you can mark a type as contravariant if it is used only as a type of formal method parameters and not used as a method return type.
You need to add out T to your interface:
public interface IBenefitStrategy<in T>