抽象类和接口具有相同的泛型方法

时间:2011-05-25 08:58:25

标签: c# generics inheritance architecture

我正在编写两个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的类使用Search<T>中定义的MyBaseClass函数。

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

注意:调用Search时,T将始终是继承的抽象类或接口的派生类。这是我在C#2.0(C# abstract class return derived type enumerator)中找到实现这一目标的唯一方法,它只会造成更多问题!

我是否有一种类型安全的方式可以实现这一点,而不使用对象和转换?

解决方案:

根据Andras Zoltan接受的答案,我在我的项目中创建了这个类,并且必须为使用这两个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
}

5 个答案:

答案 0 :(得分: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>();
        }

答案 1 :(得分:2)

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

我赞成了@ IndigoDelta的答案,但它突出了我不喜欢的整体设计 - 我怀疑你应该使用通用接口和泛型类;不是通用方法,因为它没有任何意义:

  

注意:调用Search时,T将始终是继承的抽象类或接口的派生类。

我把这个解决方案投入到混合中;我认为这更好,因为它意味着每个派生类型不需要重新实现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>
  { }
} 

答案 2 :(得分:1)

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

答案 3 :(得分: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)来组合你的约束,或者你根本不需要接口。

答案 4 :(得分: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>();