2011-05-25 61 views
3

我正在编写两个API,我将与许多项目一起使用。有些项目使用了其中一个API,另一些则使用了其中一些,但我的大部分项目都将使用这两个API。我试图将它们设计成好像它们完全分开,但我在一件事上挣扎着。具有相同通用方法的抽象类和接口

namespace FirstApi { 
    public abstract class MyBaseClass { 
     //constructor, some methods and properties 

     public IEnumerable<T> Search<T>() where T : MyBaseClass, new() { 
      //search logic here. must use generics as I create new instances of T here 
     } 
    } 
} 


namespace SecondApi { 
    public interface IMyInterface { 
     //some property and method signatures 

     IEnumerable<T> Search<T>() where T : IMyInterface, new(); 
    } 
} 

namespace MyProject { 
    public class MyDerivedClass : MyBaseClass, IMyInterface { 

    } 
} 

这两个API都需要这种搜索方法。第二个API在调用IMyInterface.Search<T>()的其他类中有一些功能,我希望那些继承MyBaseClass的类使用MyBaseClass中定义的Search<T>函数。

编译错误:类型参数方法的“T”“MyBaseClass.Search()”的约束必须为类型参数界面方法的“T”“IMyInterface.Search()”的约束条件相匹配。考虑使用显式接口实现。

注意:当搜索被调用时,T将永远是任何抽象类或接口已被继承的派生类。这是我在C#2.0(C# abstract class return derived type enumerator)中实现这一目标的唯一方法,它只是造成了更多问题!

有没有一种类型安全的方式,我可以实现这一点,而不使用对象和铸造?

解决方案:基于由安德拉斯·佐尔坦接受的答案

,我在我的项目创造了这个班,并且将不得不重新创建这个类为使用这两个API每个项目。

public abstract class ApiAdapter<TAdapter> : MyBaseClass, IMyInterface where TAdapter: MyBaseClass, IJsonObject, new() 
{ 
    IEnumerable<T> IJsonObject.Search<T>() 
    { 
     foreach (TAdapter row in base.Search<TAdapter>()) 
      yield return (T)(IMyInterface)row; 
    } 
} 

然后我就像这样继承这个类。

public class Client : ApiAdapter<Client> { 
    //everything else can go here 
} 
+0

也许它在清晨,我都还没有苏醒过来,但我不明白问题是什么。为什么你提供的代码不工作? – 2011-05-25 09:11:29

+0

对不起,我更新了编译器错误的帖子 – Connell 2011-05-25 09:16:46

+0

对不起,我也不明白这个问题。也许提供一个真实世界的例子,而不是'IMyInterface'和'MyBaseClass'可以提供帮助。 – 2011-05-25 09:17:00

回答

2

我开始回答说明为什么它不适合你,但我认为现在已经很好理解了,所以我会把它排除在外。

我已经upvoted @ IndigoDelta的答案,但它突出了一些我不喜欢这里的整体设计 - 我有一个偷偷摸摸的实际上你应该使用通用接口和泛型类;不是通用的方法,因为它没有任何意义:

Note: When Search is called, T will always be the derived class of whichever abstract class or interface has been inherited.

我把这个解决方案投入混合;我认为这样更好,因为这意味着每个派生类型都不需要重新实现IMyInterface.Search方法,并且它可以通过某种方式实际执行您提到的这个规则。这是一个泛型类型,致力于将两个API一起使用,这意味着派生类型不需要做任何事情:

namespace MyProject 
{ 
    using FirstApi; 
    using SecondApi; 

    public class SecondAPIAdapter<T2> : MyBaseClass, IMyInterface 
    where T2 : SecondAPIAdapter<T2>, new() 
    { 
    #region IMyInterface Members 
    IEnumerable<T> IMyInterface.Search<T>() 
    { 
     return Search<T2>().Cast<T>(); 
    } 
    #endregion 
    } 

    //now you simply derive from the APIAdapter class - passing 
    //in your derived type as the generic parameter. 
    public class MyDerivedClass : SecondAPIAdapter<MyDerivedClass> 
    { } 
} 
+0

这看起来像是理想的解决方案!这也意味着我可以保持两个API的设计完整。我会走开,试试这个,回到你身边。 – Connell 2011-05-25 10:31:13

+0

我喜欢这个,比我发布的解决方案更优雅,并且会防止重复的代码。 – IndigoDelta 2011-05-25 12:10:37

1

我认为你可以做显式的接口实现,当你通过IMyInterface.Search访问方法时,编译器会运行正确的方法。

4

您可以明确地实现接口搜索方法,例如

public class MyDerivedClass : BasicTestApp.FirstApi.MyBaseClass, BasicTestApp.SecondApi.IMyInterface 
    { 
     IEnumerable<T> SecondApi.IMyInterface.Search<T>() 
     { 
      // do implementation 
     } 
    } 

不过,我觉得你问的MyBaseClass搜索方法时,处理你的对象IMyInterface的部分代码调用Search<T>方法被调用。我看不到方法,因为您有两个具有不同约束条件的T类型,无法关联。 如果您在搜索方法的两个定义中都做了where T : BasicTestApp.FirstApi.MyBaseClass, IMyInterface, new();,那么您不会遇到问题,但这会将两个API绑定在一起

以下是您明确实现的接口方法的可能实现。它不能避免演员阵容,但至少保持整洁。

 IEnumerable<T> SecondApi.IMyInterface.Search<T>() 
     { 
      var results = base.Search<MyDerivedClass>(); 

      return results.Cast<T>(); 
     } 
+0

+1这是我要去做的事情 - 但我试图找到一种不固定类型的方法 - 如果可能的话,保持它与输入类型相同。因为对于一个通用函数来说,它总是没有意义的。这对我来说听起来像一个普通的类。 – 2011-05-25 09:46:31

+0

我想我喜欢它+1,但是,为什么不实现基类上的接口? – Jodrell 2011-05-25 09:56:18

+0

@Jodrell我相信这是因为FirstAPI不知道SecondAPI的任何内容 – 2011-05-25 10:01:49

1

您需要使用显式实现。

public class MyDerivedClass : MyBaseClass, IMyInterface 
{ 
    // The base class implementation of Search inherited 

    IEnumerable<T> IMyInterface.Search<T>() 
    { 
     // The interface implementation 
     throw new NotImplementedException(); 

     // this would not work because base does not implement IMyInterface 
     return base.Search<T>(); 
    }  
} 

由于实现是不同的这是有道理的。如果它们不相同,那么基类应该实现接口,并且应该使用协变(.Net 4)。0)来结合你的约束,或者,你可能根本不需要接口。

0

我希望我不是糊涂了,你能不能改变你的定义,使得:

public interface IMyInterface<in T> 
{ 
    //some property and method signatures 

    IEnumerable<U> Search<U>() where U : T, new(); 
} 

提供了T一个通用的参数,它可以用来强制执行的实现提供了搜索功能,约束类型的T

public abstract class MyBaseClass : IMyInterface<MyBaseClass> 
{ 
    public virtual IEnumerable<T> Search<T>() where T : MyBaseClass, new() 
    { 

    } 
} 

这样一来,你的派生类型是简单的:

public class MyDerivedClass : MyBaseClass 
{ 

} 

然后你就可以做搜索为:

var derived = new MyDerivedClass(); 
IMyInterface<MyDerivedClass> iface = impl; 

var results = iface.Search<MyDerivedClass>(); 
+0

这是一个好主意。通过将我的第二个API接口定义为'interface IMyInterface 其中T:IMyInterface '并定义派生类,比如'MyDerivedClass:MyBaseClass,IMyInterface ',会有一种方法吗? – Connell 2011-05-25 10:28:11

相关问题