How to use same interface two times with diferrent template parameters, in an interface?

后端 未结 6 1522
谎友^
谎友^ 2020-12-06 14:02

I Think it would be more clearer with this example. We Want to see two methods with diferrent parameters in the processor class. \"int Process (int value);\" \"double Proce

相关标签:
6条回答
  • 2020-12-06 14:11

    Here's my solution. It's based on using differentiation so you can be clear about which interface you want. You have to add an otherwise unused parameter, but that's what tells it which you want.

    public interface First { }
    public interface Second { }
    
    public class Processor : IRoot<int, double, int, double>
    {
        // Here we want 2 methods
        public int Process ( int item ) { System.Console.WriteLine ( "int Process" ); return item + 1; }
        public double Process ( double item ) { System.Console.WriteLine ( "double Process" ); return item + 10.748; }
    }
    
    public class TestProcessor : IRoot<int, int, int, int>
    {
        int IProcessWithDifferentiator<int, int, First>.Process ( int item )
        {
            System.Console.WriteLine ( "int Process" ); return item + 1;
        }
        int IProcessWithDifferentiator<int, int, Second>.Process ( int item )
        {
            System.Console.WriteLine ( "int Process" ); return item + 100302;
        }
    }
    
    public interface IProcessWithDifferentiator<TResult, TItem, TDiff>
    {
        TResult Process ( TItem item );
    }
    
    public interface IRoot<TR1, TR2, TItem1, TItem2> :
        IProcessWithDifferentiator<TR1, TItem1, First>,
        IProcessWithDifferentiator<TR2, TItem2, Second>
    {
    
    }
    
    class Program
    {
        static void Main ( string [] args )
        {
            Processor p = new Processor ();
            IProcessWithDifferentiator<int, int, First> one = p;
            System.Console.WriteLine ( "one.Process(4) = " + one.Process ( 4 ) );
            IProcessWithDifferentiator<double, double, Second> two = p;
            System.Console.WriteLine ( "two.Process(5.5) = " + two.Process ( 5.5 ) );
    
            TestProcessor q = new TestProcessor ();
            IProcessWithDifferentiator<int, int, First> q1 = q;
            System.Console.WriteLine ( "q1.Process(4) = " + q1.Process ( 4 ) );
            IProcessWithDifferentiator<int, int, Second> q2 = q;
            System.Console.WriteLine ( "q2.Process(5) = " + q2.Process ( 5 ) );
    
            System.Console.ReadLine ();
        }
    }
    

    Here's the output.

    int Process
    one.Process(4) = 5
    double Process
    two.Process(5.5) = 16.248
    int Process
    q1.Process(4) = 5
    int Process
    q2.Process(5) = 100307
    

    This will work even if you use IRoot<int,int,int,int> as you can see above; it knows (because we tell it) which IDifferentiatedProcess to use.

    (In case it matters, I'm on Visual Studio 2012.)

    0 讨论(0)
  • 2020-12-06 14:23

    Sorry to see you had so many downvotes on this, I'm facing the same issue.

    Sadly it doesn't seem to be possible - this MSDN page lists the only possible generic type constraints, and none can express the constraint "T and U can be any types, but must have distinct inheritance hierarchies" http://msdn.microsoft.com/en-us/library/d5x73970(v=vs.80).aspx

    We really need some kind of where T !: U or where T, U disjoint syntax; but as it stands there is no way to specify to the compiler that instances of TItem1 and TItem2 can never be substitutable for each other.

    0 讨论(0)
  • 2020-12-06 14:24

    So after posting my first answer to this question (#1), I then realized that one often might want to be able to convert a value from a differentiated interface to a non-differentiated interface. In other words, one wants to do the following

    IProcessWithDifferentiator<TRes, TItem, TDiff> : IProcess<TRes, TItem>
    

    but we cannot because we'll run into the same error (types might unify) when using the interface.

    I note that the OP did not specifically ask for this, but one can see that it would be next logical scenario.

    So, back to the drawing board, and returning with an ugly solution for this, which is that of having a method return the down converted type, and a proxy to support construction of such methods. (The only mitigation of ugliness is that the proxy class can be somewhat reused as shown below.) Following is the result of this exercise.

    public interface Second { }
    public interface Third { }
    
    public class Processor : IRoot<float, int, double, float, int, double>
    {
        // Here we want 3 methods
        public float Process ( float item ) { System.Console.WriteLine ( "  ...float Process..." ); return (float) (item - 55.75); }
        public int Process ( int item ) { System.Console.WriteLine ( "  ...int Process..." ); return item + 1; }
        public double Process ( double item ) { System.Console.WriteLine ( "  ...double Process..." ); return item + 10.748; }
    
        IProcess<int, int> IProcessWithDifferentiator<int, int, Second>.ConvertToBase ()
        {
            return new TP_Proxy<int, int, Second> ( this );
        }
    
        IProcess<double, double> IProcessWithDifferentiator<double, double, Third>.ConvertToBase ()
        {
            return new TP_Proxy<double, double, Third> ( this );
        }
    }
    
    public class TestProcessor : IRoot<int, int, int, int, int, int>
    {
        int IProcess<int, int>.Process ( int item )
        {
            System.Console.WriteLine ( "  ...int Process1..." ); return item - 11;
        }
        int IProcessWithDifferentiator<int, int, Second>.Process ( int item )
        {
            System.Console.WriteLine ( "  ...int Process2..." ); return item + 12;
        }
        int IProcessWithDifferentiator<int, int, Third>.Process ( int item )
        {
            System.Console.WriteLine ( "  ...int Process3..." ); return item + 100302;
        }
    
        IProcess<int, int> IProcessWithDifferentiator<int, int, Second>.ConvertToBase ()
        {
            return new TP_Proxy<int, int, Second> ( this );
        }
    
        IProcess<int, int> IProcessWithDifferentiator<int, int, Third>.ConvertToBase ()
        {
            return new TP_Proxy<int, int, Third> ( this );
        }
    }
    
    public interface IProcess<TResult, TItem>
    {
        TResult Process ( TItem item );
    }
    
    public interface IProcessWithDifferentiator<TResult, TItem, TDiff> // would love to ": IProcess<TResult, TItem>" here but won't work above
    {
        TResult Process ( TItem item ); // replicated method from IProcess... yuck(!)
        IProcess<TResult, TItem> ConvertToBase ();
    }
    
    // Having a proxy sucks.  But at least this proxy is shared among multiple classes implementing the IProcess concept.
    class TP_Proxy<TResult, TItem, TDiff> : IProcess<TResult, TItem>
    {
        public TP_Proxy ( IProcessWithDifferentiator<TResult, TItem, TDiff> px ) { _proxyTo = px; }
        private IProcessWithDifferentiator<TResult, TItem, TDiff> _proxyTo;
        TResult IProcess<TResult, TItem>.Process ( TItem item ) { return _proxyTo.Process ( item ); }
    }
    
    public interface IRoot<TR1, TR2, TR3, TItem1, TItem2, TItem3> :
        IProcess<TR1, TItem1>,
        IProcessWithDifferentiator<TR2, TItem2, Second>,
        IProcessWithDifferentiator<TR3, TItem3, Third>
    {
    }
    
    class Program
    {
        static void Main ( string [] args )
        {
            Processor p = new Processor ();
            // Direct conversion of first one, of course
            IProcess<float, float> a1 = p;
            System.Console.WriteLine ( "a1 .Process(3.3)   =    " + a1.Process ( (float) 3.3 ) );
    
            // Conversion of differentiated class
            IProcessWithDifferentiator<int, int, Second> a2 = ((IProcessWithDifferentiator<int, int, Second>) p);
            System.Console.WriteLine ( "a2d.Process(4)     =    " + a2.Process ( 4 ) );
            IProcessWithDifferentiator<double, double, Third> a3 = (IProcessWithDifferentiator<double, double, Third>) p;
            System.Console.WriteLine ( "a3d.Process(5.5)   =    " + a3.Process ( 5.5 ) );
    
            // Conversions to undifferentiated class using ugly proxies
            IProcess<int, int> a2u = ((IProcessWithDifferentiator<int, int, Second>) p).ConvertToBase ();
            System.Console.WriteLine ( "a2u.Process(4)     =    " + a2u.Process ( 4 ) );
            IProcess<double, double> a3u = ((IProcessWithDifferentiator<double, double, Third>) p).ConvertToBase ();
            System.Console.WriteLine ( "a3u.Process(5.5)   =    " + a3u.Process ( 5.5 ) );
    
            TestProcessor q = new TestProcessor ();
    
            IProcess<int, int> b1 = q;
            // Direct conversion of first one, of course
            System.Console.WriteLine ( "b1 .Process(3)     =    " + b1.Process ( 3 ) );
    
            // Conversion of differentiated class
            IProcessWithDifferentiator<int, int, Second> b2d = (IProcessWithDifferentiator<int, int, Second>) q;
            System.Console.WriteLine ( "b2d.Process(4)     =    " + b2d.Process ( 4 ) );
            IProcessWithDifferentiator<int, int, Third> b3d = (IProcessWithDifferentiator<int, int, Third>) q;
            System.Console.WriteLine ( "b3d.Process(5)     =    " + b3d.Process ( 5 ) );
    
            // Conversions to undifferentiated class using ugly proxies
            IProcess<int, int> b2u = ((IProcessWithDifferentiator<int, int, Second>) q).ConvertToBase ();
            System.Console.WriteLine ( "b2u.Process(4)     =    " + b2u.Process ( 4 ) );
            IProcess<int, int> b3u = ((IProcessWithDifferentiator<int, int, Third>) q).ConvertToBase ();
            System.Console.WriteLine ( "b3u.Process(5)     =    " + b3u.Process ( 5 ) );
    
            System.Console.ReadLine ();
        }
    }
    

    The output is as follows:

      ...float Process...
    a1 .Process(3.3)   =    -52.45
      ...int Process...
    a2d.Process(4)     =    5
      ...double Process...
    a3d.Process(5.5)   =    16.248
      ...int Process...
    a2u.Process(4)     =    5
      ...double Process...
    a3u.Process(5.5)   =    16.248
      ...int Process1...
    b1 .Process(3)     =    -8
      ...int Process2...
    b2d.Process(4)     =    16
      ...int Process3...
    b3d.Process(5)     =    100307
      ...int Process2...
    b2u.Process(4)     =    16
      ...int Process3...
    b3u.Process(5)     =    100307
    
    0 讨论(0)
  • 2020-12-06 14:28

    From what I understand, you want to define an interface like this one:

    public interface IRoot<TM, TPM, TPK>
      where TM : MType
      where TPM : PMType
      where TPK : new()
    {
      TM Get(TPK key);
      TPM Get(TPK key);
    }
    

    And this is not possible, because you can't define two methods with the same name and the same parameters.

    error CS0111: Type 'IRoot<TM,TPM,TPK>' already defines a member called 'Get' with the same parameter types
    

    Try to define your interface directly (without inheritance) and change the method names. For example:

    public interface IRoot<TM, TPM, TPK>
      where TM : MType
      where TPM : PMType
      where TPK : new()
    {
      TM GetTM(TPK key);
      TPM GetTPM(TPK key);
    }
    
    0 讨论(0)
  • 2020-12-06 14:29

    You could probably use this kind of implementation. You'll loose some generic arguments:

    public interface IBase<TM, TPkey> 
        where TM : bType
        where TPkey : new ()        
    {
        TM Get(TPkey key);
    }
    
    public interface IABase<TPK> : IBase<ConcreteTmA, TPK> {}
    public interface IBBase<TPK> : IBase<ConcreteTmB, TPK> {}
    
    public class Root <TPK> :
        IABase<TPK>,
        IBBase<TPK> 
        where TM : MType 
        where TPM : PMType 
        where TPK : new()
    {
        ConcreteTmA IABase.Get(TPK key)
        {
        }
    
        ConcreteTmB IBBase.Get(TPK key)
        {
        }
    }
    
    0 讨论(0)
  • 2020-12-06 14:30

    The problem is exactly what the error message says:

    'IRoot<TM,TPM,TPK>' cannot implement both 'IBase<TM,TPK>' and
    'IBase<TPM,TPK>' because they may unify for some type parameter substitutions
    

    For instance, you can do this:

    public class Test : IRoot<Int32, Int32, Int32>
    

    in which case it would have two inheritance chains to IBase<Int32, Int32> which is not allowed.

    As always, please try to include the problem you're facing as well as the code with the problem.

    0 讨论(0)
提交回复
热议问题