Can I define/constrain a member as implementing two interfaces, without generics?

こ雲淡風輕ζ 提交于 2020-01-14 10:37:08

问题


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?


回答1:


"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....




回答2:


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();
    }
}



回答3:


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();
}



回答4:


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.




回答5:


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.



来源:https://stackoverflow.com/questions/9037577/can-i-define-constrain-a-member-as-implementing-two-interfaces-without-generics

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!