C # - используя полиморфизм в классах, я не писал - PullRequest
2 голосов
/ 25 марта 2009

Каков наилучший способ реализации полиморфного поведения в классах, которые я не могу изменить? В настоящее время у меня есть код вроде:

if(obj is ClassA) {
    // ...
} else if(obj is ClassB) {
    // ...
} else if ...

Очевидный ответ - добавить виртуальный метод в базовый класс, но, к сожалению, код находится в другой сборке, и я не могу его изменить. Есть ли лучший способ справиться с этим, чем уродливый и медленный код выше?

Ответы [ 5 ]

9 голосов
/ 25 марта 2009

Хммм ... кажется более подходящим для Адаптер .

public interface ITheInterfaceYouNeed
{
    void DoWhatYouWant();
}

public class MyA : ITheInterfaceYouNeed
{
    protected ClassA _actualA;

    public MyA( ClassA actualA )
    {
        _actualA = actualA;
    }

    public void DoWhatYouWant()
    {
        _actualA.DoWhatADoes();
    }
}

public class MyB : ITheInterfaceYouNeed
{
    protected ClassB _actualB;

    public MyB( ClassB actualB )
    {
        _actualB = actualB;
    }

    public void DoWhatYouWant()
    {
        _actualB.DoWhatBDoes();
    }
}

Похоже, много кода, но это сделает клиентский код намного ближе к тому, что вы хотите. Кроме того, это даст вам возможность подумать о том, какой интерфейс вы на самом деле используете.

5 голосов
/ 25 марта 2009

Проверьте шаблон Посетитель . Это позволяет вам приблизиться к добавлению виртуальных методов в класс без изменения класса. Вам необходимо использовать метод расширения с динамическим приведением, если базовый класс, с которым вы работаете, не имеет метода Visit. Вот пример кода:

public class Main
{
    public static void Example()
    {
        Base a = new GirlChild();
        var v = new Visitor();
        a.Visit(v);
    }
}

static class Ext
{
    public static void Visit(this object b, Visitor v)
    {
        ((dynamic)v).Visit((dynamic)b);
    }
}

public class Visitor
{
    public void Visit(Base b)
    {
        throw new NotImplementedException();
    }

    public void Visit(BoyChild b)
    {
        Console.WriteLine("It's a boy!");
    }

    public void Visit(GirlChild g)
    {
        Console.WriteLine("It's a girl!");            
    }
}
//Below this line are the classes you don't have to change.
public class Base
{
}

public class BoyChild : Base
{
}

public class GirlChild : Base
{
}
0 голосов
/ 26 марта 2009

Посмотрите на шаблон Decorator . Нолдорин фактически объяснил это, не назвав паттерна.

Декоратор - это способ расширения поведения без наследования. Единственное, что я хотел бы изменить в коде Нолдорина, это то, что конструктор должен получить экземпляр объекта, который вы украшаете.

0 голосов
/ 26 марта 2009

Методы расширения предоставляют простой способ добавления дополнительных сигнатур методов в существующие классы. Для этого требуется структура 3.5.

Создайте статический служебный класс и добавьте что-то вроде этого:

public static void DoSomething(this ClassA obj, int param1, string param2)
{
    //do something
}

Добавьте ссылку на служебный класс на странице, и этот метод появится в качестве члена ClassA. Вы можете перегрузить существующие методы или создать новые таким образом.

0 голосов
/ 26 марта 2009

Я бы сказал, что стандартный подход здесь заключается в том, чтобы обернуть класс, который вы хотите «наследовать», в переменную защищенного экземпляра, а затем эмулировать все не приватные члены (метод / свойства / события / и т.д.) обернутого класса. в вашем классе контейнера. Затем вы можете пометить этот класс и его соответствующих членов как виртуальные, чтобы использовать с ним стандартные функции полиморфизма.

Вот пример того, что я имею в виду. ClosedClass - это класс, содержащийся в сборке, код которого у вас нет доступа.

public virtual class WrapperClass : IClosedClassInterface1, IClosedClassInterface2
{
    protected ClosedClass object;

    public ClosedClass()
    {
        object = new ClosedClass();
    }

    public void Method1()
    {
        object.Method1();
    }

    public void Method2()
    {
        object.Method2();
    }
}

Если бы любая сборка, на которую вы ссылаетесь, была спроектирована хорошо, то все типы / элементы, к которым вы, возможно, когда-либо захотите получить доступ, будут помечены соответствующим образом (абстрактные, виртуальные, запечатанные), но на самом деле это, к сожалению, не так (иногда вы можете даже испытал эту проблему с библиотекой базовых классов). На мой взгляд, класс-обертка - это то, что нужно. У него есть свои преимущества (даже когда класс, из которого вы хотите получить , наследуем ), а именно удаление / изменение модификатора методов, к которым у пользователя вашего класса нет доступа. ReadOnlyCollection<T> в BCL является довольно хорошим примером этого.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...