Как мне вызвать метод подкласса для объекта базового класса? - PullRequest
3 голосов
/ 16 апреля 2009

Я использую библиотеку, которая генерирует кучу классов для меня.

Все эти классы наследуются от общего базового класса, но этот базовый класс не определяет пару методов, общих для всех подклассов.

Например:

SubClassA : BaseClass{
  void Add(ItemA item) {...}
  ItemA CreateNewItem() {...}
}

SubClassB: BaseClass{
  void Add(ItemB item) {...}
  ItemB CreateNewItem() {...}
}

К сожалению, базовый класс не имеет этих методов . Это было бы здорово:

BaseClass{
  // these aren't actually here, I'm just showing what's missing:
  abstract void Add(ItemBaseClass item);  // not present!
  abstract ItemBaseClass CreateNewItem(); // not present!
}

Поскольку существует общий базовый класс для моих объектов A + B и общий базовый класс для объектов Item, я надеялся извлечь пользу из чудесного мира полиморфизма.

К сожалению, поскольку общие методы на самом деле отсутствуют в базовом классе, я не могу вызвать их виртуально. например, это было бы идеально:

BaseClass Obj;
Obj = GetWorkUnit(); // could be SubClassA or SubClassB

ItemBaseClass Item = Obj.CreateNewItem(); // Compile Fail: CreateNewItem() isn't in the base class

Item.DoSomething();

Obj.Add(Item); // Compile Fail: Add(...) isn't in the base class

Очевидно, что приведение в действие сработает, но тогда мне нужно будет знать, какой у меня тип, который сводит на нет преимущества.

Как я могу "вызвать" вызов этих методов? Меня не беспокоит получение объекта, который не реализует метод, который я пытаюсь вызвать. Я действительно могу делать то, что я хочу в VB - я не получаю intellisense, но компилятор доволен, и он работает:

CType(Obj, Object).Add(Item) // Note: I'm using C#--NOT VB

Агант, я не могу контролировать эти классы (которые, я думаю, исключают частичные классы).

Ответы [ 7 ]

6 голосов
/ 16 апреля 2009

Вы не можете вызвать не виртуальный метод производного класса, не прибегая к рефлексии или другим грязным уловкам. Если вы хотите сделать это, тогда это просто:

// obj is your object reference.
obj.GetType().InvokeMember("MethodName", 
    System.Reflection.BindingFlags.InvokeMethod, null, obj, null /* args */)
3 голосов
/ 16 апреля 2009

Я мог бы что-то упустить, но почему бы не создать и унаследовать интерфейс с этими методами?

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

0 голосов
/ 16 апреля 2009

Проблема № 1: ваши подклассы ничего не переопределяют Проблема №2: ваши подклассы имеют сигнатуры функций, отличные от базового класса

Убедитесь, что ваши подписи верны, и, если вы переопределяете их, отметьте их как виртуальные, а не как абстрактные. Вам нужно будет добавить пустое тело к виртуальным функциям базового класса, если вы не хотите, чтобы они что-либо делали.

class ItemBase{}

class ItemA : ItemBase{}

class ItemB : ItemBase{}

class BaseClass
{
    public virtual void Add(ItemBase item){}
    public virtual ItemBase CreateItem() { return null; }
}

class ClassA : BaseClass
{
    public override void Add(ItemBase item)
    {
        //do whatever
        throw new NotImplementedException();
    }

    public ItemBase CreateItem()
    {
        //create an ItemBase and return
        throw new NotImplementedException();
    }
}
0 голосов
/ 16 апреля 2009

Вы забыли ключевое слово "override" в определениях методов ваших подклассов. Когда что-то объявляется «абстрактным», оно по определению является виртуальным, и поэтому в подклассе необходимо поместить ключевое слово «override» перед объявлением метода. Итак, что должно работать ниже:

BaseClass{
  abstract void Add(ItemBaseClass item);  // not present!
  abstract ItemBaseClass CreateNewItem(); // not present!
}

SubClassA : BaseClass{
  override void Add(ItemA item) {...}
  override ItemA CreateNewItem() {...}
}

SubClassB: BaseClass{
  override void Add(ItemB item) {...}
  override ItemB CreateNewItem() {...}
}

Это должно работать именно так, как вы хотите в вашем примере использования.

0 голосов
/ 16 апреля 2009

Другая возможность состоит в том, чтобы использовать некоторый механизм прокси, такой как RealProxy (хотя и не очень эффективный с точки зрения производительности) или лучше, используя динамически создаваемые DynamicMethods, так что накладные расходы на использование отражения поддерживаются только один раз для каждого типа, тогда как сохраняя его таким же гибким, как метод отражения. Это окупается, когда вам нужно вызывать метод несколько раз, но требует некоторых знаний MSIL.

0 голосов
/ 16 апреля 2009

Если вы используете Visual Studio 2008 - вы можете создать метод расширения для базового класса. Внутри этого метода расширения вы выполняете приведение и вызываете метод подкласса. Это должно работать и для проектов, ориентированных на платформу 2.0, если вы компилируете с помощью VS 2008. Заимствуя другой предложенный код отражения, вы можете сделать следующее ...

public static class MyExtensions
{
   public static ItemBaseClass CreateNewItem(this BaseClass item)
   {
      return item.GetType().InvokeMember("MethodName", System.Reflection.BindingFlags.InvokeMethod, null, obj, null /* args */);
   }
}
0 голосов
/ 16 апреля 2009

Объявите их как виртуальные:

http://msdn.microsoft.com/en-us/library/9fkccyh4(VS.71).aspx

...