【问题标题】:Having trouble getting rid of redundant code via inheritance or generics难以通过继承或泛型摆脱冗余代码
【发布时间】:2011-03-09 08:14:37
【问题描述】:

抱歉标题含糊不清,但我不知道如何用一句话来概括这一点。我有很多冗余 C# 代码的情况,它看起来确实像某种巧妙的技巧,使用某些继承或泛型属性可以解决这个问题。但是,我不是一个非常有经验的程序员(尤其是 C#),只是不太了解解决方案。

情况,以简化的形式,看起来像这样。我有一堆类都继承自一种类型。

public class Foo : SuperFoo
{
     ...
     public Foo SomeMethod() { ... }
}
public class Bar : SuperFoo
{
     ...
     public Bar SomeMethod() { ... }
}
public class Baz : SuperFoo
{
     ...
     public Baz SomeMethod() { ... }
}
...    
public class SuperFoo
{
     ...
}

当需要处理这些对象的集合时,问题就出现了。我的第一稿解决方案(糟糕的)如下所示:

public void SomeEventHasHappened(...)
{
     ProcessFoos();
     ProcessBars();
     ProcessBazes();
     ...
}

public void ProcessFoos()
{
     ...
     foreach (var foo in fooList)
     {
          ...
          foo.SomeMethod();
     }
}
public void ProcessBars()
{
     ...
     foreach (var bar in barList)
     {
          ...
          bar.SomeMethod();
     }
}

...等等。问题是 ProcessX 方法中的所有代码基本上都是相同的,除了正在操作的对象的类型。出于显而易见的原因,将所有这些合并到一种方法中会很好。

我的第一个想法是创建一个通用的 Process() 方法,该方法将 List<SuperFoo> 作为参数,然后从那里继续。问题是泛型 SuperFoo 没有 SomeMethod(),它也没有 SomeMethod(),因为每个子类的 SomeMethod() 都有不同的返回类型,所以覆盖不起作用。

【问题讨论】:

  • 我见过返回object的代码,然后转换为适当的类型。
  • 虽然这可以解决我在这里提出的问题,但我应该澄清一下...我宁愿不让SomeMethod() 返回一般的object,因为SomeMethod() 是除了这部分代码之外,还需要在许多其他地方调用,这需要在所有其他地方进行强制转换。不过,感谢您的建议。

标签: c# .net oop


【解决方案1】:

我通常会添加一个对基本类型进行操作的接口。

interface ISuperFoo
{
    public ISuperFoo SomeMethod() { ... }
}

public class Foo : SuperFoo, ISuperFoo
{
     // concrete implementation
     public Foo SomeMethod() { ... }

     // method for generic use, call by base type
     public ISuperFoo ISuperFoo.SomeMethod() 
     { 
       return SomeMethod(); 
     }
}

public void Processs()
{
     ...
     foreach (var iSuperFoo in list)
     {
          ...
          iSuperFoo.SomeMethod();
     }
}

当然,这取决于您使用结果的目的。

有时您可以更轻松地使用泛型,但您也可能会陷入混乱。有时,在某个地方沮丧会更容易。当然,只要你能负担得起,你就会尽量避免这种情况。

【讨论】:

  • 这很不错,可能是我应该想到的!感谢您的回答。
【解决方案2】:

这是一个如何使用泛型并使 SuperFoo 成为抽象类的示例。

public interface ISuperFoo
{
    ...
}

public abstract class SuperFoo<T> where T : ISuperFoo
{
    public abstract T SomeMethod();
}

public class BazReturn : ISuperFoo
{
    ...
}

public class Baz: SuperFoo<BazReturn>
{
    public override BazReturn SomeMethod()
    {
        throw new NotImplementedException();
    }
}

public class BarReturn : ISuperFoo
{
    ...
}

public class Bar : SuperFoo<BarReturn>
{
    public override BarReturn SomeMethod()
    {
        throw new NotImplementedException();
    }
}

public static class EventHandler
{
    public static void SomeEventHasHappened(List<SuperFoo<ISuperFoo>> list)
    {
        foreach (SuperFoo<ISuperFoo> item in list)
        {
            ISuperFoo result = item.SomeMethod();
        }
    }
}

如果需要,您可以将 ISuperFoo 接口替换为具体的类,但是您必须强制转换返回值,这会违背目的。

public abstract class SuperFoo<T>
{
    public abstract T SomeMethod();
}

public class Foo : SuperFoo<int>
{
    public override int SomeMethod()
    {
        throw new NotImplementedException();
    }
}

public static class EventHandler
{
    public static void SomeEventHasHappened(List<SuperFoo<int>> list)
    {
        foreach (SuperFoo<int> item in list)
        {
            item.SomeMethod();
        }
    }
}

【讨论】:

  • 感谢您的回复。但是我对答案的第一部分中的 FooReturn 和 Foo 类有点困惑。如果我理解正确的话,看起来 Foo 的 SomeMethod() 的一个实例将返回一个 FooReturn 对象而不是一个 Foo 对象。因此,以前在 Foo 中的所有其他代码现在应该在 FooReturn 中,而 Foo 只需要包含 SomeMethod()。如果确实是这样,这不是在其他具有Foo fooObject = anotherFooObject.SomeMethod(); 的地方中断代码吗?
  • SomeMethod 的返回类型应始终被视为返回 ISuperFoo。您会注意到抽象 SuperFoo 的通用变量 T 必须扩展 ISuperFoo。这就是提供所需的一致返回类型的原因。我在我的代码中添加了一个 Bar 示例,希望能够澄清。
  • 我还将子类从 Foo 重命名为 Baz,希望能进一步阐明接口、抽象和扩展类型之间的区别。
猜你喜欢
  • 1970-01-01
  • 2017-11-22
  • 1970-01-01
  • 2013-07-24
  • 2011-11-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-03
相关资源
最近更新 更多