Извините за неопределенный заголовок, но я не был уверен, как суммировать это в одной фразе. У меня есть ситуация с большим количеством избыточного кода 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 является тем же кроме типа объектов, на которых управляют. Было бы хорошо консолидировать все их в один метод по очевидным причинам.
Моя первая мысль состояла в том, чтобы просто сделать универсальный Процесс () методом, который взял a List<SuperFoo>
в качестве параметра и только продолжите двигаться оттуда. Проблема состоит в том, что универсальный SuperFoo не имеет SomeMethod (), и он не может иметь того, потому что каждый из SomeMethod дочерних классов () имеет различный тип возврата, так наличие переопределений не работает.
Обычно я добавляю интерфейс, работающий с базовыми типами.
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();
}
}
Конечно, это зависит от того, для чего вы используете результат.
Иногда вы можете упростить использование универсальных шаблонов, но также можете попасть в беспорядок. Иногда проще куда-то унизить. Конечно, вы стараетесь избегать этого, когда можете себе это позволить.
Вот пример того, как это могло бы работать с использованием дженериков и превращения 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();
}
}
}