【问题标题】:How to use same interface two times with diferrent template parameters, in an interface?如何在一个界面中使用不同的模板参数两次使用相同的界面?
【发布时间】:2012-09-12 03:02:01
【问题描述】:

我认为这个例子会更清楚。我们希望在处理器类中看到两种具有不同参数的方法。 “int过程(int值);” "双进程(双值);"

但是编译器对 IRoot 说: “Generics.IRoot”不能同时实现“Generics.IProcess”和“Generics.IProcess”,因为它们可能会针对某些类型参数替换进行统一。

public class Processor : IRoot<int, double, int, double>
{
    // Here we want 2 methods
    public int Process(int item) { }
    public double Process(double item) { }
}

public interface IProcess<TResult, TItem>
{
    TResult Process(TItem item);
}

public interface IRoot<TR1, TR2, TItem1, TItem2> :
    IProcess<TR1, TItem1>,
    IProcess<TR2, TItem2>
{

}

【问题讨论】:

  • 有什么问题?从您的代码中绝对不清楚...提​​供一些细节和解释,而不仅仅是一段代码!
  • 问题在标题中:“如何在一个界面中使用不同的模板参数两次使用相同的界面?”
  • 问题是我们可以尝试找出为什么你不能这样做的唯一方法是将代码复制并粘贴到编译器中,然后尝试编译它。您应该发布您从编译器获得的错误消息,以便人们更容易回答。永远不要认为人们会为了帮助您、帮助我们帮助您、为我们提供尽可能多的信息而费尽心思。

标签: c# generics


【解决方案1】:

这是我的解决方案。它基于使用差异化,因此您可以清楚自己想要哪个界面。您必须添加一个其他未使用的参数,但这就是告诉它您想要的参数。

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

这是输出。

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

即使您使用IRoot&lt;int,int,int,int&gt;,这也将起作用,如您所见;它知道(因为我们告诉它)要使用哪个 IDifferentiatedProcess。

(以防万一,我使用的是 Visual Studio 2012。)

【讨论】:

  • 富有想象力的解决方案,看起来是我们用 C# 能做到的最好的解决方案
  • 我已经发布了第二个答案,解决了删除差异化参数的问题,但遗憾的是使用代理有点难看。
【解决方案2】:

所以在发布我对这个问题(#1) 的第一个答案后,我意识到人们通常可能希望能够将值从差异化接口转换为非差异化接口。换句话说,一个人想做以下事情

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

但我们不能,因为我们在使用界面时会遇到同样的错误(类型可能会统一)。

我注意到 OP 并没有特别要求这样做,但可以看出这将是下一个合乎逻辑的场景。

所以,回到绘图板,并为此返回一个丑陋的解决方案,即让方法返回向下转换的类型,以及支持构建此类方法的代理。 (减少丑陋的唯一方法是代理类可以在某种程度上被重用,如下所示。)以下是这个练习的结果。

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

输出如下:

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

【讨论】:

    【解决方案3】:

    问题正是错误信息所说的:

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

    例如,您可以这样做:

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

    在这种情况下,IBase&lt;Int32, Int32&gt; 会有两个继承链,这是不允许的。

    与往常一样,请尝试将您面临的问题以及代码包含问题。

    【讨论】:

      【解决方案4】:

      据我了解,您想定义这样的接口:

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

      这是不可能的,因为你不能定义两个具有相同名称和相同参数的方法。

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

      尝试直接定义您的接口(不继承)并更改方法名称。例如:

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

      【讨论】:

        【解决方案5】:

        很抱歉看到您对此投了这么多反对票,我也面临同样的问题。

        遗憾的是,这似乎不可能 - 这个 MSDN 页面列出了唯一可能的泛型类型约束,没有一个可以表达约束“TU 可以是任何类型,但必须具有不同的继承层次结构” http://msdn.microsoft.com/en-us/library/d5x73970(v=vs.80).aspx

        我们确实需要某种where T !: Uwhere T, U disjoint 语法;但就目前而言,无法向编译器指定 TItem1TItem2 的实例永远不能相互替代。

        【讨论】:

        • 是的,我也是。来自所有可能的外太空,您需要特殊的语言能力来命名或识别您可能将对象投射到的各个基础,以便即使在统一时也可以将它们分开。
        【解决方案6】:

        您可能会使用这种实现方式。你会丢失一些通用参数:

        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)
            {
            }
        }
        

        【讨论】:

        • 我不明白你的代码,你有没有 {..} 块的接口,你有一个具有方法实现的接口,你在 IRoot 中的通用参数约束比你有通用参数等。您应该放弃“可能”这个词,并至少使代码能够编译。
        • “可能的词”是因为这种解决方案只有在 TM 可能的类型数量有限并且没有所有通用参数的 IRoot 是可以接受的情况下才可行。
        猜你喜欢
        • 1970-01-01
        • 2010-12-31
        • 2015-03-17
        • 2020-08-15
        • 2010-11-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多