The "inbound" parameters of a universal class and the general parameters of a method can combine types, but there is no means for variables or fields to represent "composite" types. In addition, in order to pass an object to a generic type parameter that combines several constraints, the object must be passed to a type that actually implements all of these constraints. It can be tricky.
For example, suppose the classes Foo and Bar implement Intf1 and Intf2 . One wants to write a function AddToList<T>(thing as T) where T:Intf1,Intf2 . Such a function perfectly accepts objects such as Foo or Bar . Suppose, however, that we want to use such a function to add all objects to the same list (this can be a combination of Foo , Bar and any number of other types that are also implemented by Intf1 and Intf2 ), and then pass these objects to functions whose parameter is also limited to implement both Intf1 and Intf2 . You could apply any object that turned out to be Foo , and apply any object that turned out to Bar , but if other types are written that also handle Intf1 and Intf2 , it would be difficult to handle them.
You can solve the problem somewhat inconveniently without using Reflection or other similar tricks. Define the IActUpon<Base1, Base2> interface using the ActUpon<thingType>ActUpon(thingType thing) where thingType: Base1, Base2 . Implementations of such a method will be able to pass the thing parameter to other methods that require a general method parameter limited to Base1 and Base2 . The biggest difficulties with this approach are that you need to write separate code for each possible number of restrictions, and that in many places where you would use a lambda expression, you need to write an implementation of IActUpon... .
source share