Search code examples
c#genericsinterfacetype-conversionstrong-typing

How to ensure in C# that the type of an object is equal to this?


Problem

I have the following interface (it can be changed, but just to give the idea):

public interface IObj<T>
{
    void Merge(IObj<T> other);
}

The problem is with the Merge operation. I am not able to find a way to ensure that the parameter passed to the method is of the same type that this. For example have a look at the following implementation:

public class A<T> : IObj<T>
{
    public void Merge(IObj<T> other)
    {
        var casted = other as A<T>;
        if (casted == null)
            throw new ArgumentException("Incorrect type.");

        // do the actual stuff
    }
}

Any object implementing the interface will always require to be merged with an instance of the same type. Therefore I need to write this boilerplate code to try casting before I do anything.

Question

Is it possible to ensure this by contract / interface / anything else?


Solution

  • From the @nsinreal solution, I propose you one other level on interface (and an abstract class, to avoid implementing several time the interfaces):

    public interface IObj<T>
    {
        void Merge(IObj<T> other);
    }
    
    public interface IObj<T, TImplementor> : IObj<T>
        where TImplementor : class, IObj<T, TImplementor>
    {
        void Merge(TImplementor other);
    }
    
    public abstract class AObj<T, TImplementor> : IObj<T, TImplementor>
        where TImplementor : class, IObj<T, TImplementor>
    {
        public abstract void Merge(TImplementor other);
    
        void IObj<T>.Merge(IObj<T> other)
        {
            var casted = other as TImplementor;
            if (casted == null)
                throw new ArgumentException("Incorrect type.");
            Merge(casted);
        }
    }
    
    public class A<T> : AObj<T, A<T>>
    {
        override public void Merge(A<T> other)
        {
            // do the actual stuff
        }
    }
    
    public class B<T> : AObj<T, B<T>>
    {
        override public void Merge(B<T> other)
        {
            // do the actual stuff
        }
    }
    

    Now, for a class called SpecificObj<T>, you have both methods:

    void Merge(IObj<T>)
    void Merge(SpecificObj<T>)
    

    and you can still use it as an IObj<T>

    Still not sure that completely answer to your problem.