Class “如何在对象Pascal中生成”;“接口类”;(或“接口的接口”类型

Class “如何在对象Pascal中生成”;“接口类”;(或“接口的接口”类型,class,interface,delphi,Class,Interface,Delphi,看看这个例子: //---------------------------------------------------------------------------- type ISomeInterface = interface procedure SomeMethod; end; // this is wrong, but illustrates that, what i need: TSomeClassWhichImplementsSomeInterfac

看看这个例子:

//----------------------------------------------------------------------------
type

  ISomeInterface = interface
    procedure SomeMethod;
  end;

  // this is wrong, but illustrates that, what i need:
  TSomeClassWhichImplementsSomeInterface = class of ISomeInterface;

var
  gHardCodedPointer: Pointer; // no matter

procedure Dummy(ASomeClassToWorkWith: TSomeClassWhichImplementsSomeInterface);
begin
  // actually, type of ASomeClassToWorkWith is unknown (at least TObject), but it
  // must implement SomeMethod, so i can make something like this:
  ASomeClassToWorkWith(gHardCodedPointer).SomeMethod;
end;

...

type

  TMyClass = class(TInterfacedObject, ISomeInterface)
  end;

...

// TMyClass implements ISomeInterface, so i can pass it into Dummy:
Dummy(TMyClass);
//----------------------------------------------------------------------------
当然,我可以继承TMyClass并在childs中使用它,但我不需要这个。我想使用另一个具有自己层次结构的类,只是将ISomeInterface的实现添加到这些类中(因为在Object Pascal中没有多重继承,就像在C++中一样)。
我知道这可能看起来很疯狂,不要问我为什么需要这个,只要说——它可能是要实现或不实现。非常感谢

我认为您必须使用接口,而不是类:

procedure Dummy(ASomeClassToWorkWith: ISomeInterface); 
begin 
  // actually, type of ASomeClassToWorkWith is unknown (at least TObject), but it 
  // must implement SomeMethod, so i can make something like this: 
  ASomeClassToWorkWith.SomeMethod; 
end; 
您只需考虑引用计数

如果您确实需要对象实例,可以如下更改接口:


为什么不能使用接口引用? 但是,假设有一个很好的理由,这可能会有所帮助

正如您所发现的,您不能在接口上执行
类的操作

此外,您不能使用变量值将任何内容转换为其他内容。强制转换是硬连线的,告诉编译器您知道要强制转换的引用是特定类型的。尝试使用变量(如
ASomeClassToWorkWith
参数)这样做会产生错误,因为这违背了强制转换的本质

下面的代码不是我推荐的,但它可以编译,我认为它可以满足您的需要。它所做的是使用一个“虚拟”祖先,并使用polymorfism让编译器调用正确类型的方法。如果您没有将SomeMethod标记为virtual,那么在两次单击按钮时都会得到虚拟祖先的消息

接口中的实例函数向您展示了一种无需使用RTTI就可以访问接口的实现实例的方法。在使用接口委派时请注意这一点:您可能无法获得预期的实例

type
  TForm1 = class(TForm)
    TSomethingBtn: TButton;
    TMyClassBtn: TButton;
    procedure FormCreate(Sender: TObject);
    procedure TSomethingBtnClick(Sender: TObject);
    procedure TMyClassBtnClick(Sender: TObject);
  private
    { Private declarations }
    FSomething: TObject;
    FMyClass: TObject;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

type
  TSomething = class; // forward;
  TSomethingClass = class of TSomething;

  ISomeInterface = interface
    procedure SomeMethod;
    function Instance: TSomething;
  end;

  TSomething = class(TInterfacedObject, ISomeInterface)
    procedure SomeMethod; virtual;
    function Instance: TSomething;
  end;

var
  gHardCodedPointer: Pointer; // no matter

procedure Dummy(aSomething: TSomething);
begin
  // actually, type of ASomeClassToWorkWith is unknown (at least TObject), but it
  // must implement SomeMethod, so i can make something like this:
  aSomething.SomeMethod;
end;

type
  TMyClass = class(TInterfacedObject, ISomeInterface)
    procedure SomeMethod; virtual;
    function Instance: TSomething;
  end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  FSomething := TSomething.Create;
  FMyClass := TMyClass.Create;
end;

{ TMyClass }

function TMyClass.Instance: TSomething;
begin
  Result := TSomething(Self);
end;

procedure TMyClass.SomeMethod;
begin
  ShowMessage('This comes from TMyClass');
end;

{ TSomething }

function TSomething.Instance: TSomething;
begin
  Result := Self;
end;

procedure TSomething.SomeMethod;
begin
  ShowMessage('This comes from the "dummy" ancestor TSomething');
end;

procedure TForm1.TMyClassBtnClick(Sender: TObject);
begin
  // Presume this has been set elsewhere
  gHardCodedPointer := FMyClass;
  Dummy(TSomething(gHardCodedPointer));
end;

procedure TForm1.TSomethingBtnClick(Sender: TObject);
begin
  // Presume this has been set elsewhere
  gHardCodedPointer := FSomething;
  Dummy(TSomething(gHardCodedPointer));
end;

即使可以,也不能使用接口变量对接口进行类型转换

与类一样,当您键入指向元类的指针时,您将得到元类(class of)类型的内容,而不是元类中的类型


对于类,您可以通过将类型转换到层次结构中最低的公共类来解决这个问题。您可以对接口执行相同的操作。。。如果他们彼此继承。

我想你想要的是:

procedure Dummy; 
var Intf : ISomeInterface;
begin
  if Assigned(gHardCodedPointer) and Supports(gHardCodedPointer,ISomeInterface,Intf) then
    Intf.SomeMethod
end;

如果不是的话,我不知道你想在那里实现什么…

看来我知道你想做什么了。您只需使用MS和合作伙伴在接口核心中实现的东西,即使用GUI。下面是一个示例,但您肯定应该在IDE中使用自己的guid和CTRL+SHIFT+G

  ...

  type
    ITestInterface = interface
     ['{2EA2580F-E5E5-4F3D-AF90-2BBCD65B917B}']
      procedure DoSomething;
    end;

    TTestObject = class(TInterfacedObject, ITestInterface)
      procedure DoSomething;
    end;

    TTestObject2 = class(TInterfacedObject, ITestInterface)
      procedure DoSomething;
    end;

  ...

  procedure TestMethod(Obj: TInterfacedObject);
  var
    Intf: ITestInterface;
  begin
    if (Obj as IUnknown).QueryInterface(ITestInterface, Intf) = S_OK then
      Intf.DoSomething;
  end;

  { TTestObject }

  procedure TTestObject.DoSomething;
  begin
    MessageDlg('This is TTestObject showing something', mtInformation, [mbOk], 0)
  end;

  { TTestObject2 }

  procedure TTestObject2.DoSomething;
  begin
    MessageDlg('This is TTestObject2 showing something', mtInformation, [mbOk], 0)
  end;

  procedure TForm2.Button1Click(Sender: TObject);
  var
    Obj1, Obj2: TInterfacedObject;
  begin
    Obj1:=TTestObject.Create;
    Obj2:=TTestObject2.Create;

    TestMethod(Obj1);
    TestMethod(Obj2);
  end;

当通过接口变量或通过指向实现同一接口方法的类实例的变量调用代码时,不同之处在于使用了不同的虚拟方法表(VMT),即在接口的VMT中只有接口方法(当然还有AddRef、Release和QI),在类的VMT中,将有该类的所有虚拟方法。 这意味着你的代码

ASomeClassToWorkWith(gHardCodedPointer).SomeMethod;
将被编译为直接调用tsomeclasswhichiplementssomeinterface.SomeMethod,而不是通过接口指针在ISomeInterface的VMT中调用virtual方法

而且,由于接口不能声明类方法和类属性,接口类型不是对象(而类是对象),因此“接口类”没有任何意义

您可以添加中间抽象类,并将您的“接口类”声明为中间类的类:

type
  TInterfacedObjectWithISomeInterface = class(TInterfacedObject, ISomeInterface)
    procedure SomeMethod; virtual; abstract;
  end;

  TSomeClassWhichImplementsSomeInterface = class of TInterfacedObjectWithISomeInterface;

procedure Dummy(ASomeClassToWorkWith: TSomeClassWhichImplementsSomeInterface);

...

type

  TMyClass = class(TInterfacedObjectWithISomeInterface)
    procedure SomeMethod; override;
  end;

您可以声明元类,但不能根据基类实现的接口来定义它们。只能在运行时检查接口实现

您可以向
Dummy
函数传递一个元类,但不能使用该元类将普通指针类型转换为更具体的类型。类型转换是一个编译时操作,但是元类参数的实际值直到运行时才知道。最好是将其类型转换到元类的基类。然后可以调用该基类中定义的所有方法

但似乎您实际上并不关心基类是什么,只要该类实现了您的接口。在这种情况下,可以忽略元类参数。键入将指针强制转换为
TObject
(或者,最好首先将
gHardCodedPointer
声明为
TObject
),然后使用
Supports
函数获取接口引用

var
  SupportsInterface: Boolean;
  Some: ISomeInterface;
begin
  SupportsInterface := Supports(TObject(gHardCodedPointer), ISomeInterface, Some);
  Assert(SupportsInterface, 'Programmer stored bad class instance in gHardCodedPointer');
  Some.SomeMethod;
end;
如果您真的关心元类参数,那么也可以为它添加一些强制。您可以检查给定类是否实现了接口,并且可以检查
gHardCodedPointer
中的对象是否是该类的实例:

Assert(ASomeClassToWorkWith.GetInterfaceEntry(ISomeInterface) <> nil);
Assert(TObject(gHardCodedPointer).InheritsFrom(ASomeClassToWorkWith));
Assert(ASomeClassToWorkWith.GetInterfaceEntry(ISomeInterface)nil);
断言(TObject(gHardCodedPointer).InheritsFrom(ASomeClassToWorkWith));
但是请注意,您不需要检查这些结果中的任何一个,就可以在
gHardCodedPointer
上调用
SomeMethod
。它们其实并不重要


顺便说一下,在Delphi中,您希望拥有的唯一硬编码指针值是
nil
。所有其他指针值都是在编译时很难预测的地址,因为编译器、链接器和加载程序都决定了内存中所有内容的实际位置。我建议您为该变量提供一些其他名称,以便更准确地描述它真正包含的内容。

否,在这种情况下,我必须传递一个对象引用(但我需要该对象的一个类)。添加了用于返回实现接口的对象的示例。(如果您只需要类,然后更改,然后返回类型从TObject到TClass)我看不到“gHardCodedPointer”的会议,或者您只是想这样做吗I Dummy:ISomeInterface(gHardCodedPointer)。SomeMethod;它是gHardCoded的类吗
Assert(ASomeClassToWorkWith.GetInterfaceEntry(ISomeInterface) <> nil);
Assert(TObject(gHardCodedPointer).InheritsFrom(ASomeClassToWorkWith));