28

Let's assume I have two classes that look like this (the first block of code and the general problem are related to C#):

class A 
{
    public int IntProperty { get; set; }
}

class B 
{
    public int IntProperty { get; set; }
}

These classes cannot be changed in any way (they are part of a 3rd party assembly). Therefore, I cannot make them implement the same interface, or inherit the same class that would then contain IntProperty.

I want to apply some logic on the IntProperty property of both classes, and in C++ I could use a template class to do that quite easily:

template <class T>
class LogicToBeApplied
{
    public:
        void T CreateElement();

};

template <class T>
T LogicToBeApplied<T>::CreateElement()
{
    T retVal;
    retVal.IntProperty = 50;
    return retVal;
}

And then I could do something like this:

LogicToBeApplied<ClassA> classALogic;
LogicToBeApplied<ClassB> classBLogic;
ClassA classAElement = classALogic.CreateElement();
ClassB classBElement = classBLogic.CreateElement();   

That way I could create a single generic factory class that would work for both ClassA and ClassB.

However, in C#, I have to write two classes with two different where clauses even though the code for the logic is exactly the same:

public class LogicAToBeApplied<T> where T : ClassA, new()
{
    public T CreateElement()
    {
        T retVal = new T();
        retVal.IntProperty = 50;
        return retVal;
    }
}

public class LogicBToBeApplied<T> where T : ClassB, new()
{
    public T CreateElement()
    {
        T retVal = new T();
        retVal.IntProperty = 50;
        return retVal;
    }
}

I know that if I want to have different classes in the where clause, they need to be related, i.e. to inherit the same class, if I want to apply the same code to them in the sense that I described above. It is just that it is very annoying to have two completely identical methods. I also do not want to use reflection because of the performance issues.

Can somebody suggest some approach where this can be written in a more elegant fashion?

  • 3
    Why are you using generics for this in the first place? There's nothing generic about those two functions. – Luaan Nov 3 '16 at 11:41
  • 1
    @Luaan This is a simplified example of a variation of abstract factory pattern. Imagine there are dozens of classes that inherit ClassA or ClassB, and that ClassA and ClassB are abstract classes. Inherited classes carry no additional information, and need to be instantiated. Instead of writing a factory for each of them, I opted to use generics. – Vladimir Stokic Nov 3 '16 at 13:11
  • 6
    Well, you could use reflection or dynamics if you're confident that they're not going to break it in future releases. – Casey Nov 3 '16 at 13:20
  • This is actually my biggest complaint about generics is that it can't do this. – Joshua Nov 3 '16 at 21:09
  • 1
    @Joshua, I think of it more as an issue with interfaces not supporting "duck typing." – Ian Nov 3 '16 at 23:50
49

Add a proxy interface (sometimes called an adapter, occasionally with subtle differences), implement LogicToBeApplied in terms of the proxy, then add a way to construct an instance of this proxy from two lambdas: one for the property get and one for the set.

interface IProxy
{
    int Property { get; set; }
}
class LambdaProxy : IProxy
{
    private Function<int> getFunction;
    private Action<int> setFunction;
    int Property
    {
        get { return getFunction(); }
        set { setFunction(value); }
    }
    public LambdaProxy(Function<int> getter, Action<int> setter)
    {
        getFunction = getter;
        setFunction = setter;
    }
}

Now, whenever you need to pass in an IProxy but have an instance of the third party classes, you can just pass in some lambdas:

A a = new A();
B b = new B();
IProxy proxyA = new LambdaProxy(() => a.Property, (val) => a.Property = val);
IProxy proxyB = new LambdaProxy(() => b.Property, (val) => b.Property = val);
proxyA.Property = 12; // mutates the proxied `a` as well

Additionally, you can write simple helpers to construct LamdaProxy instances from instances of A or B. They can even be extension methods to give you a "fluent" style:

public static class ProxyExtension
{
    public static IProxy Proxied(this A a)
    {
      return new LambdaProxy(() => a.Property, (val) => a.Property = val);
    }

    public static IProxy Proxied(this B b)
    {
      return new LambdaProxy(() => b.Property, (val) => b.Property = val);
    }
}

And now construction of proxies looks like this:

IProxy proxyA = new A().Proxied();
IProxy proxyB = new B().Proxied();

As for your factory, I'd see if you can refactor it into a "main" factory method that accepts an IProxy and performs all logic on it and other methods that just pass in new A().Proxied() or new B().Proxied():

public class LogicToBeApplied
{
    public A CreateA() {
      A a = new A();
      InitializeProxy(a.Proxied());
      return a; // or maybe return the proxy if you'd rather use that
    }

    public B CreateB() {
      B b = new B();
      InitializeProxy(b.Proxied());
      return b;
    }

    private void InitializeProxy(IProxy proxy)
    {
        proxy.IntProperty = 50;
    }
}

There's no way to do the equivalent of your C++ code in C# because C++ templates rely on structural typing. As long as two classes have the same method name and signature, in C++ you can call that method generically on both of them. C# has nominal typing - the name of a class or interface is part of its type. Therefore, the classes A and B cannot be treated the same in any capacity unless an explicit "is a" relationship is defined through either inheritance or interface implementation.

If the boilerplate of implementing these methods per class is too much, you can write a function that takes an object and reflectively builds a LambdaProxy by looking for a specific property name:

public class ReflectiveProxier 
{
    public object proxyReflectively(object proxied)
    {
        PropertyInfo prop = proxied.GetType().GetProperty("Property");
        return new LambdaProxy(
            () => prop.GetValue(proxied),
            (val) => prop.SetValue(proxied, val));
     }
}

This fails abysmally when given objects of incorrect type; reflection inherently introduces the possibility of failures the C# type system cannot prevent. Luckily you can avoid reflection until the maintenance burden of the helpers becomes too great because you're not required to modify the IProxy interface or the LambdaProxy implementation to add the reflective sugar.

Part of the reason this works is that LambdaProxy is "maximally generic"; it can adapt any value that implements the "spirit" of the IProxy contract because the implementation of LambdaProxy is completely defined by the given getter and setter functions. It even works if the classes have different names for the property, or different types that are sensibly and safely representable as ints, or if there's some way to map the concept that Property is supposed to represent to any other features of the class. The indirection provided by the functions gives you maximal flexibility.

  • A very interesting approach, and can definitely be used for calling the functions, but can it be used for factory, where I actually need to create objects of ClassA and ClassB? – Vladimir Stokic Nov 3 '16 at 9:12
  • @VladimirStokic See edits, I've expanded a bit on this – Jack Nov 3 '16 at 9:38
  • surely this method still requires you to explicitly map the property for each type with the added possibility of a runtime error if your mapping function is buggy – Ewan Nov 3 '16 at 10:56
  • As an alternative to the ReflectiveProxier, could you could build a proxy using the dynamic keyword? Seems to me you'd have the same fundamental problems (i.e. errors that are only caught at runtime), but the syntax and maintainability would be much simpler. – Bobson Nov 3 '16 at 12:55
  • 1
    @Jack - Fair enough. I added my own answer demoing it. It's a very useful feature, in certain rare circumstances (like this one). – Bobson Nov 3 '16 at 23:52
12

Here is an outline how to use adapters without inheriting from A and/or B, with the possibility of using them for existing A and B objects:

interface IAdapter
{
    int Property { get; set; }
}

class LogicToBeApplied<T> where T : IAdapter, new()
{
    public T Create()
    {
        var ret = new T();
        ret.Property = 50;
        return ret;
    }
}

class AAdapter : IAdapter
{
    A _a;

    public AAdapter()  // use this if you want to have the "logic" part create new objects
    {
        _a=new A();
    }

    public AAdapter(A a) // if you need an adapter for an existing object afterwards
    {
       _a=a;
    }

    public int Property
    {
        get { return _a.Property; }
        set { _a.Property = value; }
    }

    public A {get{return _a; } } // to provide access for non-generic code
}

class BAdapter 
{
     // analogously
}

I would typically prefer this kind of object adapter over class proxies, they avoid of ugly problems you can run into with inheritance. For example, this solution will work even if A and B are sealed classes.

  • Why new int Property? you're not shadowing anything. – pinkfloydx33 Nov 3 '16 at 23:14
  • @pinkfloydx33: just a typo, changed it, thanks. – Doc Brown Nov 4 '16 at 7:37
9

You could adapt ClassA and ClassB through a common interface. This way your code in LogicAToBeApplied stays the same. Not much different than what you have though.

class A
{
    public int Property { get; set; }
}
class B
{
    public int Property { get; set; }
}

interface IAdapter
{
    int Property { get; set; }
}

class LogicToBeApplied<T> where T : IAdapter, new()
{
    public T Create()
    {
        var ret = new T();
        ret.Property = 50;
        return ret;
    }
}

class AAdapter : A, IAdapter { }

class BAdapter : B, IAdapter { }
  • 1
    +1 using the Adapter Pattern is the traditional OOP solution here. It's more of an adapter than a proxy since we adapt the A, B types to a common interface. The big advantage is that we do not have to duplicate the common logic. The disadvantage is that the logic now instantiates the wrapper/proxy instead of the actual type. – amon Nov 3 '16 at 9:03
  • 5
    The problem with this solution is that you cannot simply take two objects of type A and B, convert them somehow to AProxy and BProxy, and then apply LogicToBeApplied to them. This problem can be solved by using aggregation instead of inheritance (resp. implement the proxy objects not by deriving from A and B, but by taking a reference to A and B objects). Again an example of how wrong use of inheritance causes problems. – Doc Brown Nov 3 '16 at 9:06
  • @DocBrown How would that go in this particular case? – Vladimir Stokic Nov 3 '16 at 9:08
  • 1
    @Jack: these kind of solution makes sense when LogicToBeApplied has a certain complexity and should not be repeated at two places in the code base under no circumstances. Then the additional boilerplate code is often negligible. – Doc Brown Nov 3 '16 at 9:24
  • 1
    @Jack Where's the redundancy? The two classes don't have a common interface. You create wrappers that do have a common interface. You use that common interface to implement your logic. It's not like the same redundancy doesn't exist in the C++ code - it's just hidden behind a bit of code generation. If you feel that strongly about things that look the same, even though they aren't the same, you can always use T4s or some other templating system. – Luaan Nov 3 '16 at 11:44
8

The C++ version only works because its templates use “static duck typing” – anything compiles as long as the type provides the correct names. It is more like a macro system. The generics system of C# and other languages works very differently.

devnull's and Doc Brown's answers show how the adapter pattern can be used to keep your algorithm general, and still operate on arbitrary types … with a couple of restrictions. Notably, you're now creating a different type than you actually want.

With a bit of trickery, it is possible to use exactly the intended type without any changes. However, we now need to extract all interactions with the target type into a separate interface. Here, these interactions are construction and property assignment:

interface IInteractions<T> {
  T Instantiate();
  void AssignProperty(T target, int value);
}

In an OOP interpretation, this would be an example of the strategy pattern, though mixed with generics.

We can then rewrite your logic to use these interactions:

public class LogicBToBeApplied<T>
{
    public T CreateElement(IInteractions<T> interactions)
    {
        T retVal = interactions.Instantiate();
        interactions.AssignProperty(retVal, 50);
        return retVal;
    }
}

The interaction definitions would look like:

class Interactions_ClassA : IInteractions<ClassA> {
  public override ClassA Instantiate() { return new ClassA(); }
  public override void AssignProperty(ClassA target, int value) { target.IntProperty = value; }
}

The big disadvantage of this approach is that the programmer needs to write and pass an interaction instance when calling the logic. This is fairly similar to adapter-pattern based solutions, but is slightly more general.

In my experience, this is the closest you can get to template functions in other languages. Similar techniques are used in Haskell, Scala, Go, and Rust to implement interfaces outside of a type definition. However, in these languages the compiler steps in and selects the correct interaction instance implicitly so you don't actually see the extra argument. This is also similar to C#'s extension methods, but is not restricted to static methods.

  • Interesting approach. Not the one which would be my first choice, but I guess it can have some advantages when writing a framework or something like that. – Doc Brown Nov 3 '16 at 12:54
8

If you really want to throw caution to the wind, you can use "dynamic" to make the compiler take care of all the reflection nastiness for you. This will result in a runtime error if you pass an object to SetSomeProperty that does not have a property named SomeProperty.

using System;

namespace ConsoleApplication3
{
    class A
    {
        public int SomeProperty { get; set; }
    }

    class B
    {
        public int SomeProperty { get; set; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var a = new A();
            var b = new B();

            SetSomeProperty(a, 7);
            SetSomeProperty(b, 12);

            Console.WriteLine($"a.SomeProperty = {a.SomeProperty}, b.SomeProperty = {b.SomeProperty}");
        }

        static void SetSomeProperty(dynamic obj, int value)
        {
            obj.SomeProperty = value;
        }
    }
}
4

The other answers correctly identify the problem and provide workable solutions. C# does not (generally) support "duck typing" ("If it walks like a duck..."), so there's no way to force your ClassA and ClassB to be interchangeable if they weren't designed that way.

However, if you're already willing to accept the risk of a runtime fault, then there's an easier answer than using Reflection.

C# has the dynamic keyword which is perfect for situations like this. It tells the compiler "I won't know what type this is until runtime (and maybe not even then), so allow me to do anything at all to it".

Using that, you can build exactly the function you want:

public class LogicToBeApplied<T> where T : new()
{
    public static T CreateElement()
    {
        dynamic retVal = new T(); // This doesn't care what type T is.
        retVal.IntProperty = 50;  // This will fail at runtime if there is no "IntProperty" 
                                  // or it doesn't accept an int.
        return retVal;            // Once again, we don't care what it is.
    }
}

Note the use of the static keyword as well. That lets you use this as:

A classAElement = LogicToBeApplied<A>.CreateElement();
B classBElement = LogicToBeApplied<B>.CreateElement();

There are no big-picture performance implications of using dynamic, the way there is the one-time hit (and added complexity) of using Reflection. The first time your code hits the dynamic call with a specific type will have a small amount of overhead, but repeated calls will be just as fast as standard code. However, you will get a RuntimeBinderException if you try to pass in something that doesn't have that property, and there's no good way to check that ahead of time. You might want to specifically handle that error in a useful manner.

  • This can be slow, but often slow code is not a problem. – Ian Nov 3 '16 at 23:45
  • @Ian - Good point. I added a bit more about the performance. It's not actually as bad as you'd think, provided you're reusing the same classes in the same spots. – Bobson Nov 3 '16 at 23:51
  • Remember in C++ templates don't even have the overhead of virtual methods! – Ian Nov 4 '16 at 1:20
2

You can use reflection to pull out the property by name.

public class logic 
{
    public object getNew<T>() where T : new()
    {
        T ret = new T();
        try
        {
            var property = typeof(T).GetProperty("IntProperty");
            if (property != null && property.PropertyType == typeof(int))
            {
                property.SetValue(ret, 50);
            }
        }
        catch (AmbiguousMatchException)
        {
            //hmm..
        }
        return ret;
    }
}

Obviously you risk a runtime error with this method. Which is what C# is trying to stop you doing.

I did read somewhere that a future version of C# will allow you to pass objects as an interface that they don't inherit but do match. Which would also solve your problem.

(I'll try and dig up the article)

Another method, although I'm not sure it saves you any code, would be to subclass both A and B and also inherit an Interface with IntProperty.

public interface IIntProp {
    public int IntProperty {get, set}
}

public class A2 : A, IIntProp {}

public class B2 : B, IIntProp {}
  • Possibility of runtime error and performance issues are the reasons why I did not want to go with reflection. I am, however, very interested in reading the article you mentioned in your answer. Looking forward to reading it. – Vladimir Stokic Nov 3 '16 at 8:32
  • 1
    surely you take the same risk with your c++ solution? – Ewan Nov 3 '16 at 8:34
  • 4
    @Ewan no, c++ checks for the member at compile time – Caleth Nov 3 '16 at 8:57
  • Reflection means optimization problems and (much more importantly) hard to debug runtime errors. Inheritance and a common interface means declaring a subclass ahead of time for every single one of these classes (no way to make one anonymously on the spot) and doesn't work if they don't use the same property name every time. – Jack Nov 3 '16 at 9:12
  • 1
    @Jack there are downsides, but consider that reflection is used extensively in Mappers, Serializers, Dependency Injection frameworks etc and that the goal is to do it with the least amount of code duplication – Ewan Nov 3 '16 at 9:24
0

I just wanted to use implicit operator conversions together with the delegate/lambda approach of Jack's answer. A and B are as assumed:

// A and B are mutable reference types

class A
{
  public int IntProperty { get; set; }
}

class B
{
  public int IntProperty { get; set; }
}

Then it is easy to get nice syntax with implicit user-defined conversions (no extension methods or similar needed):

// Adapter is an immutable type. However, the delegate instances have a captured reference to an A or a B (closure semantics)
struct Adapter
{
  readonly Func<int> getter;
  readonly Action<int> setter;

  Adapter(Func<int> getter, Action<int> setter)
  {
    this.getter = getter;
    this.setter = setter;
  }

  public int IntProperty
  {
    get { return getter(); }
    set { setter(value); }
  }

  public static implicit operator Adapter(A a) => new Adapter(() => a.IntProperty, x => a.IntProperty = x);
  public static implicit operator Adapter(B b) => new Adapter(() => b.IntProperty, x => b.IntProperty = x);

  public A CloneToA() => new A { IntProperty = getter(), };
  public B CloneToB() => new B { IntProperty = getter(), };
}

Illustration of use:

class LogicToBeApplied
{
  public static A CreateA()
  {
    var a = new A();
    Initialize(a);
    return a;
  }
  public static B CreateB()
  {
    var b = new B();
    Initialize(b);
    return b;
  }

  static void Initialize(Adapter a)
  {
    a.IntProperty = 50;
  }
}

The Initialize method shows how you can work with Adapter without caring about whether it is an A or a B or something else. The invokations of the Initialize method show that we do not need any (visible) cast or .AsProxy() or similar to treat concrete A or B as an Adapter.

Consider if you want to throw an ArgumentNullException in the user-defined conversions if the argument passed is a null reference, or not.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.