2

我有 100 个类,它们有一些相似的元素和一些独特的元素。我创建了一个接口来命名那些类似的项目,例如:接口 IAnimal。我通常会做的是:

class dog : IAnimal

但是有 100 个课程,我不想通过所有课程来寻找可以应用 IAnimal 的课程。

我想做的是:

dog scruffy = new dog();
cat ruffles = new cat();

IAnimal[] animals = new IAnimal[] {scruffy as IAnimal, ruffles as IAnimal} // gives null

或者

IAnimal[] animals = new IAnimal[] {(IAnimal)scruffy, (IAnimal)ruffles} //throws exception

然后做

foreach (IAnimal animal in animals)
{
   animal.eat();
}

有没有办法让 c# 让我将褶边和邋遢视为 IAnimal 而不必在编写类时编写:IAnimal。

谢谢!

编辑(不懒惰):这些类是从 sql 存储的 proc 元数据生成的,这意味着每次生成它时我都必须返回并添加它们,或者修改代码生成器以识别接口中的成员,实际上那不是一个坏主意。我希望有某种泛型方法或其他东西。

4

11 回答 11

10

您所要求的称为鸭子类型,恐怕不是 C# 的一部分。任何解决方案都将涉及反射和查看属性,我认为手动检查类会更快。

不过,这将是一个有趣的项目。

于 2009-01-22T17:35:04.403 回答
6

您可以使用部分类来解决这个问题:让机器生成/重新生成的代码位于每个类的一个源文件中,而手动编码的部分(定义 IAnimal 的子类)在另一个源文件中。

于 2009-01-22T17:43:23.137 回答
4

解决办法是修改代码生成器。目前它对于您的需求来说太简单了。如果类中存在相关的属性和/或方法,它应该添加接口实现。

于 2009-01-22T17:34:16.480 回答
4

您可以创建一个eat通过反射访问“”的适配器,这是一个穷人的鸭子打字:

public class Adapter<T> : IAnimal
{
   private T x;

   Adapter(T x)
   {
     this.x = x;
   }

   public void eat()
   {
     x.GetType().GetMethod("eat").Invoke(this);
   }
}

然后你可以像这样使用它:

dog scruffy = new dog();
cat ruffles = new cat();

IAnimal[] animals = new IAnimal[] {new Adapter<dog>(scruffy), new Adapter<cat>(ruffles )};
于 2009-01-22T17:57:16.277 回答
1

假设你真的不能修改 cat 类有充分的理由,

您可以为继承自 IAnimal 的 cat 编写适配器,即:

  class cat_adapter : IAnimal
  {
       private cat baseCat;
       public cat_adapter( cat aCat)
       {
           baseCat = aCat;
       }

       // Implement IAnimal interface and redirect to baseCat
       public void eat()
       {
            baseCat.munchOnMeowMix();
       }

  }

在 C++ 中,您可以使用模板,假设所有生成的类都需要调用相同的函数:

  template <class BaseType>
  class CAnimalAdapter : public IAnimal
  {
  private:
        BaseType* m_baseInstance;
  public:
        CAnimalAdapter(BaseType* baseInstance) :
            m_baseInstance(baseInstance)
        {
        }

        void eat()
        {
            // You will get a compiler error if BaseType doesn't have this implemented
            baseInstance->doEat();                
        }

  }

也许比我更 C#-py 的人可以对反射做同样的事情。

于 2009-01-22T17:35:27.320 回答
1

我知道这并不容易,您可以使用反射进行后期绑定调用,但最好花时间编辑类或编写宏来执行此操作。

于 2009-01-22T17:35:32.827 回答
1

如果代码生成器将类生成为部分,您可以添加另一个实现接口的部分定义。

于 2009-01-22T18:09:23.273 回答
0

不,如果没有具体的基类或接口,你就无法让 C# 让你这样做。

于 2009-01-22T17:34:19.723 回答
0

如果您已经实现了该方法并且只想实现接口,则可以在 IDE 中使用带有替换文件命令的正则表达式。

否则,请查看扩展方法。它们可能适合您的需要。

于 2009-01-22T17:35:38.660 回答
0

你的类必须实现接口 IAnimal。两个不同类上的同名方法/属性不被认为是等效的,除非它们都是接口/基类的实现。

于 2009-01-22T17:37:58.047 回答
0

如果将生成的类实现为部分类,则可以在这些类上实现接口,然后将代码生成器更改为仅生成部分类的一半。这将允许您在不丢失接口实现(或您决定添加的任何其他自定义逻辑)的情况下重新生成类。

这种方法是 LINQ to SQL 生成类文件的方式。

于 2009-01-22T17:46:15.207 回答