2

I have a main class and several inherited classes that implement a method with the same name, like this:

MainClass = class(TImage)
  //main class methods...
end;

MyClass1 = class(MainClass)
  procedure DoSomething;
end;

MyClass2 = class(MainClass)
  procedure DoSomething;
end;

MyClass3 = class(MainClass)
  procedure DoSomething;
end;

I also have a TList containing pointers to object instances (of several classes). If I want to call the right DoSomething procedure for each class, do I use the following?

if TList[i] is MyClass1 then
  MyClass1(TList[i]).DoSomething
else if TList[i] is MyClass2 then
  MyClass2(TList[i]).DoSomething
else if TList[i] is MyClass3 then
  MyClass3(TList[i]).DoSomething

Is there some casting method that allows me to do this in a few lines of code?

2 Answers 2

10

Yes, virtual polymorphism :)

MainClass = class(TImage)
  procedure DoSomething; virtual;
end;

MyClass1 = class(MainClass)
  procedure DoSomething; override;
end;

MyClass2 = class(MainClass)
  procedure DoSomething; override;
end;

MyClass3 = class(MainClass)
  procedure DoSomething; override;
end;

And then just:

if TList[i] is MainClass then
  MainClass(TList[i]).DoSomething

If you don't want to do an empty MainClass.DoSomething procedure, you can also mark it virtual; abstract;.

Sign up to request clarification or add additional context in comments.

2 Comments

Polymorphism not inheritance... to be specific
real polymorphism or virtual? ;)
4

The virtual inheritance answer is the best for the situation you described where the classes descend from a common base class, but if you have a situation where there is not a common base class between your classes and you need this behavior, you can use interfaces instead to achieve the same result:

  IMainInterface = interface
    ['{0E0624C7-85F5-40AF-ADAC-73B7D79C264E}']
    procedure DoSomething;
  end;

  MyClass = class(TInterfacedObject, IMainInterface)
    procedure DoSomething;
    destructor Destroy; override;
  end;

  MyClass2 = class(TInterfacedObject, IMainInterface)
    procedure DoSomething;
  end;

  MyClass3 = class(TInterfacedObject, IMainInterface)
    procedure DoSomething;
  end;

and then using it would look something like this:

var
  i: integer;
  list: TInterfaceList;
  main: IMainInterface;
begin
  list := TInterfaceList.Create;

  list.Add(MyClass.create);
  list.Add(MyClass2.Create);
  list.Add(MyClass3.Create);

  for i := 0 to 2 do
    if Supports(list[i], IMainInterface, main) then
      main.DoSomething;

  list.Free;

3 Comments

Supports() has overloaded versions that return the interface pointer if found, so you do not have to call the as operator, which would redundantly invoke the same interface lookup a second time.
Thanks, been too long since I messed with interfaces. I've revised it per your suggestion.
You can declare list as IInterfaceList, this will add reference counting and save the list.Free (if you leave it as TInterfaceList, do not forget to protect it using try .. finally)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.