How to use a generic with two different classes but one of them from the VCL
I am maintaining and extending a legacy app which works with 8bits bitmaps but for several reasons I did my own bitmap class from scratch (TMyBitmap) implementing some obvious features like Width, Height and ScanLine properties. Be aware that there is no relation between TBitmap and TMyBitmap (TMyBitmap doesn't inherited from TBitmap).
Now I want to do an utility class with several methods to work with either a standard TBitmap or my own TMyBitmap. The implementation of those methods is the same for a TBitmap or a TMyBitmap as they use only the "common" properties (Width, Height and ScanLine) so the generics looks like a suitable solution:
TBmpUtility<T> = class
strict private
FBmp: T;
public
constructor Create(Bmp: T);
procedure Fill(Y: Integer; Code: Byte); //example of an utility method
end;
procedure TBmpUtility<T>.Fill(Y: Integer; Code: Byte);
var
i: Integer;
Line: PByteArray;
begin
Line := Self.FBmp.ScanLine[Y];
for i := 0 to Self.FBmp.Width - 1 do
Line[i] := Code;
end;
The obvious problem here is that I can't really call neither Self.FBmp.ScanLine[Y] nor Self.FBmp.Width because the compiler knows nothing about TBitmap or TMyBitmap at this point. Then I declared an interface and implemented it on TMyBitmap:
IBitmap = interface
['{029D42A0-132F-4C6E-BE72-648E1361C0A4}']
function GetWidth: Integer;
procedure SetWidth(Value: Integer);
function GetHeight: Integer;
procedure SetHeight(Value: Integer);
function GetScanLine(Y: Integer): Pointer;
property Width: Integer read GetWidth write SetWidth;
property Height: Integer read GetHeight write SetHeight;
property ScanLine[Y: Integer]: Pointer read GetScanLine;
end;
TMyBitmap = class (TSinglentonImplementation, IBitmap)
...
end;
Then I can add a type constraint on generic declaration to be able to call those methods through Self.FBmp from utility methods implementations.
TBmpUtility<T: IBitmap> = class
The problem here is: the standard TBitmap doesn't implement my IBitmap interface. So I can't use the utility class with a standard bitmap like follow:
Bmp := TBitmap.Create();
BmpUtil := TBmpUtility<TBitmap>.Create(Bmp);
I could do something like this:
TCustomBitmap = class (TBitmap, IBitmap)
...
end;
but the project code is using TBitmap everywhere, not TCustomBitmap so I can't interchange these types, so this is not an option.
I tried with a helper class. Something like these:
TBitmapHelper = class (IBitmap) helper for TBitmap
...
end;
but obviously, it doesn't work
Any idea how to fix this situation? Remember the goal from the beginning: having only one implementation code block for utility methods to deal with both TBitmap and TMyBitmap. If generics are the right solution, it should look like this:
Bmp := TBitmap.Create();
BmpUtil := TBmpUtility<TBitmap>.Create(Bmp);
BmpUtil.Fill(10, 0);
//or
MyBmp := TMyBitmap.Create();
BmpUtil := TBmpUtility<TMyBitmap>.Create(MyBmp);
BmpUtil.Fill(10, 0);
(FYI: I am using Delphi 10.2.3 [Tokyo])
delphi
add a comment |
I am maintaining and extending a legacy app which works with 8bits bitmaps but for several reasons I did my own bitmap class from scratch (TMyBitmap) implementing some obvious features like Width, Height and ScanLine properties. Be aware that there is no relation between TBitmap and TMyBitmap (TMyBitmap doesn't inherited from TBitmap).
Now I want to do an utility class with several methods to work with either a standard TBitmap or my own TMyBitmap. The implementation of those methods is the same for a TBitmap or a TMyBitmap as they use only the "common" properties (Width, Height and ScanLine) so the generics looks like a suitable solution:
TBmpUtility<T> = class
strict private
FBmp: T;
public
constructor Create(Bmp: T);
procedure Fill(Y: Integer; Code: Byte); //example of an utility method
end;
procedure TBmpUtility<T>.Fill(Y: Integer; Code: Byte);
var
i: Integer;
Line: PByteArray;
begin
Line := Self.FBmp.ScanLine[Y];
for i := 0 to Self.FBmp.Width - 1 do
Line[i] := Code;
end;
The obvious problem here is that I can't really call neither Self.FBmp.ScanLine[Y] nor Self.FBmp.Width because the compiler knows nothing about TBitmap or TMyBitmap at this point. Then I declared an interface and implemented it on TMyBitmap:
IBitmap = interface
['{029D42A0-132F-4C6E-BE72-648E1361C0A4}']
function GetWidth: Integer;
procedure SetWidth(Value: Integer);
function GetHeight: Integer;
procedure SetHeight(Value: Integer);
function GetScanLine(Y: Integer): Pointer;
property Width: Integer read GetWidth write SetWidth;
property Height: Integer read GetHeight write SetHeight;
property ScanLine[Y: Integer]: Pointer read GetScanLine;
end;
TMyBitmap = class (TSinglentonImplementation, IBitmap)
...
end;
Then I can add a type constraint on generic declaration to be able to call those methods through Self.FBmp from utility methods implementations.
TBmpUtility<T: IBitmap> = class
The problem here is: the standard TBitmap doesn't implement my IBitmap interface. So I can't use the utility class with a standard bitmap like follow:
Bmp := TBitmap.Create();
BmpUtil := TBmpUtility<TBitmap>.Create(Bmp);
I could do something like this:
TCustomBitmap = class (TBitmap, IBitmap)
...
end;
but the project code is using TBitmap everywhere, not TCustomBitmap so I can't interchange these types, so this is not an option.
I tried with a helper class. Something like these:
TBitmapHelper = class (IBitmap) helper for TBitmap
...
end;
but obviously, it doesn't work
Any idea how to fix this situation? Remember the goal from the beginning: having only one implementation code block for utility methods to deal with both TBitmap and TMyBitmap. If generics are the right solution, it should look like this:
Bmp := TBitmap.Create();
BmpUtil := TBmpUtility<TBitmap>.Create(Bmp);
BmpUtil.Fill(10, 0);
//or
MyBmp := TMyBitmap.Create();
BmpUtil := TBmpUtility<TMyBitmap>.Create(MyBmp);
BmpUtil.Fill(10, 0);
(FYI: I am using Delphi 10.2.3 [Tokyo])
delphi
1
I don't think generics are useful here. You need to create an adapter class that implements the interface by forwarding calls to the TBitmap instance.
– David Heffernan
Nov 25 '18 at 2:53
1
Related: Invoke method on generic type?
– Remy Lebeau
Nov 25 '18 at 6:41
add a comment |
I am maintaining and extending a legacy app which works with 8bits bitmaps but for several reasons I did my own bitmap class from scratch (TMyBitmap) implementing some obvious features like Width, Height and ScanLine properties. Be aware that there is no relation between TBitmap and TMyBitmap (TMyBitmap doesn't inherited from TBitmap).
Now I want to do an utility class with several methods to work with either a standard TBitmap or my own TMyBitmap. The implementation of those methods is the same for a TBitmap or a TMyBitmap as they use only the "common" properties (Width, Height and ScanLine) so the generics looks like a suitable solution:
TBmpUtility<T> = class
strict private
FBmp: T;
public
constructor Create(Bmp: T);
procedure Fill(Y: Integer; Code: Byte); //example of an utility method
end;
procedure TBmpUtility<T>.Fill(Y: Integer; Code: Byte);
var
i: Integer;
Line: PByteArray;
begin
Line := Self.FBmp.ScanLine[Y];
for i := 0 to Self.FBmp.Width - 1 do
Line[i] := Code;
end;
The obvious problem here is that I can't really call neither Self.FBmp.ScanLine[Y] nor Self.FBmp.Width because the compiler knows nothing about TBitmap or TMyBitmap at this point. Then I declared an interface and implemented it on TMyBitmap:
IBitmap = interface
['{029D42A0-132F-4C6E-BE72-648E1361C0A4}']
function GetWidth: Integer;
procedure SetWidth(Value: Integer);
function GetHeight: Integer;
procedure SetHeight(Value: Integer);
function GetScanLine(Y: Integer): Pointer;
property Width: Integer read GetWidth write SetWidth;
property Height: Integer read GetHeight write SetHeight;
property ScanLine[Y: Integer]: Pointer read GetScanLine;
end;
TMyBitmap = class (TSinglentonImplementation, IBitmap)
...
end;
Then I can add a type constraint on generic declaration to be able to call those methods through Self.FBmp from utility methods implementations.
TBmpUtility<T: IBitmap> = class
The problem here is: the standard TBitmap doesn't implement my IBitmap interface. So I can't use the utility class with a standard bitmap like follow:
Bmp := TBitmap.Create();
BmpUtil := TBmpUtility<TBitmap>.Create(Bmp);
I could do something like this:
TCustomBitmap = class (TBitmap, IBitmap)
...
end;
but the project code is using TBitmap everywhere, not TCustomBitmap so I can't interchange these types, so this is not an option.
I tried with a helper class. Something like these:
TBitmapHelper = class (IBitmap) helper for TBitmap
...
end;
but obviously, it doesn't work
Any idea how to fix this situation? Remember the goal from the beginning: having only one implementation code block for utility methods to deal with both TBitmap and TMyBitmap. If generics are the right solution, it should look like this:
Bmp := TBitmap.Create();
BmpUtil := TBmpUtility<TBitmap>.Create(Bmp);
BmpUtil.Fill(10, 0);
//or
MyBmp := TMyBitmap.Create();
BmpUtil := TBmpUtility<TMyBitmap>.Create(MyBmp);
BmpUtil.Fill(10, 0);
(FYI: I am using Delphi 10.2.3 [Tokyo])
delphi
I am maintaining and extending a legacy app which works with 8bits bitmaps but for several reasons I did my own bitmap class from scratch (TMyBitmap) implementing some obvious features like Width, Height and ScanLine properties. Be aware that there is no relation between TBitmap and TMyBitmap (TMyBitmap doesn't inherited from TBitmap).
Now I want to do an utility class with several methods to work with either a standard TBitmap or my own TMyBitmap. The implementation of those methods is the same for a TBitmap or a TMyBitmap as they use only the "common" properties (Width, Height and ScanLine) so the generics looks like a suitable solution:
TBmpUtility<T> = class
strict private
FBmp: T;
public
constructor Create(Bmp: T);
procedure Fill(Y: Integer; Code: Byte); //example of an utility method
end;
procedure TBmpUtility<T>.Fill(Y: Integer; Code: Byte);
var
i: Integer;
Line: PByteArray;
begin
Line := Self.FBmp.ScanLine[Y];
for i := 0 to Self.FBmp.Width - 1 do
Line[i] := Code;
end;
The obvious problem here is that I can't really call neither Self.FBmp.ScanLine[Y] nor Self.FBmp.Width because the compiler knows nothing about TBitmap or TMyBitmap at this point. Then I declared an interface and implemented it on TMyBitmap:
IBitmap = interface
['{029D42A0-132F-4C6E-BE72-648E1361C0A4}']
function GetWidth: Integer;
procedure SetWidth(Value: Integer);
function GetHeight: Integer;
procedure SetHeight(Value: Integer);
function GetScanLine(Y: Integer): Pointer;
property Width: Integer read GetWidth write SetWidth;
property Height: Integer read GetHeight write SetHeight;
property ScanLine[Y: Integer]: Pointer read GetScanLine;
end;
TMyBitmap = class (TSinglentonImplementation, IBitmap)
...
end;
Then I can add a type constraint on generic declaration to be able to call those methods through Self.FBmp from utility methods implementations.
TBmpUtility<T: IBitmap> = class
The problem here is: the standard TBitmap doesn't implement my IBitmap interface. So I can't use the utility class with a standard bitmap like follow:
Bmp := TBitmap.Create();
BmpUtil := TBmpUtility<TBitmap>.Create(Bmp);
I could do something like this:
TCustomBitmap = class (TBitmap, IBitmap)
...
end;
but the project code is using TBitmap everywhere, not TCustomBitmap so I can't interchange these types, so this is not an option.
I tried with a helper class. Something like these:
TBitmapHelper = class (IBitmap) helper for TBitmap
...
end;
but obviously, it doesn't work
Any idea how to fix this situation? Remember the goal from the beginning: having only one implementation code block for utility methods to deal with both TBitmap and TMyBitmap. If generics are the right solution, it should look like this:
Bmp := TBitmap.Create();
BmpUtil := TBmpUtility<TBitmap>.Create(Bmp);
BmpUtil.Fill(10, 0);
//or
MyBmp := TMyBitmap.Create();
BmpUtil := TBmpUtility<TMyBitmap>.Create(MyBmp);
BmpUtil.Fill(10, 0);
(FYI: I am using Delphi 10.2.3 [Tokyo])
delphi
delphi
edited Nov 24 '18 at 23:41
Delmo
asked Nov 24 '18 at 23:22
DelmoDelmo
1,17711121
1,17711121
1
I don't think generics are useful here. You need to create an adapter class that implements the interface by forwarding calls to the TBitmap instance.
– David Heffernan
Nov 25 '18 at 2:53
1
Related: Invoke method on generic type?
– Remy Lebeau
Nov 25 '18 at 6:41
add a comment |
1
I don't think generics are useful here. You need to create an adapter class that implements the interface by forwarding calls to the TBitmap instance.
– David Heffernan
Nov 25 '18 at 2:53
1
Related: Invoke method on generic type?
– Remy Lebeau
Nov 25 '18 at 6:41
1
1
I don't think generics are useful here. You need to create an adapter class that implements the interface by forwarding calls to the TBitmap instance.
– David Heffernan
Nov 25 '18 at 2:53
I don't think generics are useful here. You need to create an adapter class that implements the interface by forwarding calls to the TBitmap instance.
– David Heffernan
Nov 25 '18 at 2:53
1
1
Related: Invoke method on generic type?
– Remy Lebeau
Nov 25 '18 at 6:41
Related: Invoke method on generic type?
– Remy Lebeau
Nov 25 '18 at 6:41
add a comment |
1 Answer
1
active
oldest
votes
As David suggested, you could make an adapter class. This class could implement your interface and act (in most cases) as a simple passthrough to the actual bitmap class. The one for TBitmap could look like this (some methods left out for brevity):
type
TBitmapAdapter = class(TInterfacedObject, IBitmap)
private
FBitmap: TBitmap;
function GetWidth: Integer;
procedure SetWidth(Value: Integer);
// And everything else from IBitmap
public
constructor Create(ABitmap: TBitmap);
end;
function TBitmapAdapter.GetWidth: Integer;
begin
Result := FBitmap.Width;
end;
procedure TBitmapAdapter.SetWidth(Value: Integer);
begin
FBitmap.Width := Value;
end;
You could make a single class that handles both types, but that would require ifs in each of the methods. I would make two separate classes, so you can simply create a TBitmapAdapter or a TMyBitmapAdapter.
You can simply construct that class and pass it around as interface. If your TBmpUtility would accept an IBitmap, you can construct it like this:
TBmpUtility.Create(TBitmapAdapter.Create(Self))
You could indeed write class helpers for TBitmap and TMyBitmap to create the right wrapper for you. Again the TBitmap version:
type
TBitmapHelper = class helper for TBitmap
function AsIBitmap: IBitmap;
end;
function TBitmapHelper.AsIBitmap: IBitmap;
begin
Result := TBitmapAdapter.Create(Self);
end;
You can then simply use it like this:
TBmpUtility.Create(Bitmap.AsIBitmap)
Or maybe just pass it to the methods of the utility class, you can have one utility instance which can handle any bitmap:
BmpUtility.Fill(Bitmap.AsIBitmap);
The reference to IBitmap can be stored temporarily to prevent the adapter being created and destroyed on each call:
var b: IBitmap;
begin
b := Bitmap.AsIBitmap;
BmpUtility.SetHeight(b, 100);
BmpUtility.SetWidth(b, 100);
BmpUtility.Fill(b);
end;
The result of Bitmap.AsIBitmap is the IBitmap interface of the adapter class, but the code that uses it won't even know, and the interface is refcounted, so the adapter will be freed as soon as it goes out of scope.
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53463253%2fhow-to-use-a-generic-with-two-different-classes-but-one-of-them-from-the-vcl%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
As David suggested, you could make an adapter class. This class could implement your interface and act (in most cases) as a simple passthrough to the actual bitmap class. The one for TBitmap could look like this (some methods left out for brevity):
type
TBitmapAdapter = class(TInterfacedObject, IBitmap)
private
FBitmap: TBitmap;
function GetWidth: Integer;
procedure SetWidth(Value: Integer);
// And everything else from IBitmap
public
constructor Create(ABitmap: TBitmap);
end;
function TBitmapAdapter.GetWidth: Integer;
begin
Result := FBitmap.Width;
end;
procedure TBitmapAdapter.SetWidth(Value: Integer);
begin
FBitmap.Width := Value;
end;
You could make a single class that handles both types, but that would require ifs in each of the methods. I would make two separate classes, so you can simply create a TBitmapAdapter or a TMyBitmapAdapter.
You can simply construct that class and pass it around as interface. If your TBmpUtility would accept an IBitmap, you can construct it like this:
TBmpUtility.Create(TBitmapAdapter.Create(Self))
You could indeed write class helpers for TBitmap and TMyBitmap to create the right wrapper for you. Again the TBitmap version:
type
TBitmapHelper = class helper for TBitmap
function AsIBitmap: IBitmap;
end;
function TBitmapHelper.AsIBitmap: IBitmap;
begin
Result := TBitmapAdapter.Create(Self);
end;
You can then simply use it like this:
TBmpUtility.Create(Bitmap.AsIBitmap)
Or maybe just pass it to the methods of the utility class, you can have one utility instance which can handle any bitmap:
BmpUtility.Fill(Bitmap.AsIBitmap);
The reference to IBitmap can be stored temporarily to prevent the adapter being created and destroyed on each call:
var b: IBitmap;
begin
b := Bitmap.AsIBitmap;
BmpUtility.SetHeight(b, 100);
BmpUtility.SetWidth(b, 100);
BmpUtility.Fill(b);
end;
The result of Bitmap.AsIBitmap is the IBitmap interface of the adapter class, but the code that uses it won't even know, and the interface is refcounted, so the adapter will be freed as soon as it goes out of scope.
add a comment |
As David suggested, you could make an adapter class. This class could implement your interface and act (in most cases) as a simple passthrough to the actual bitmap class. The one for TBitmap could look like this (some methods left out for brevity):
type
TBitmapAdapter = class(TInterfacedObject, IBitmap)
private
FBitmap: TBitmap;
function GetWidth: Integer;
procedure SetWidth(Value: Integer);
// And everything else from IBitmap
public
constructor Create(ABitmap: TBitmap);
end;
function TBitmapAdapter.GetWidth: Integer;
begin
Result := FBitmap.Width;
end;
procedure TBitmapAdapter.SetWidth(Value: Integer);
begin
FBitmap.Width := Value;
end;
You could make a single class that handles both types, but that would require ifs in each of the methods. I would make two separate classes, so you can simply create a TBitmapAdapter or a TMyBitmapAdapter.
You can simply construct that class and pass it around as interface. If your TBmpUtility would accept an IBitmap, you can construct it like this:
TBmpUtility.Create(TBitmapAdapter.Create(Self))
You could indeed write class helpers for TBitmap and TMyBitmap to create the right wrapper for you. Again the TBitmap version:
type
TBitmapHelper = class helper for TBitmap
function AsIBitmap: IBitmap;
end;
function TBitmapHelper.AsIBitmap: IBitmap;
begin
Result := TBitmapAdapter.Create(Self);
end;
You can then simply use it like this:
TBmpUtility.Create(Bitmap.AsIBitmap)
Or maybe just pass it to the methods of the utility class, you can have one utility instance which can handle any bitmap:
BmpUtility.Fill(Bitmap.AsIBitmap);
The reference to IBitmap can be stored temporarily to prevent the adapter being created and destroyed on each call:
var b: IBitmap;
begin
b := Bitmap.AsIBitmap;
BmpUtility.SetHeight(b, 100);
BmpUtility.SetWidth(b, 100);
BmpUtility.Fill(b);
end;
The result of Bitmap.AsIBitmap is the IBitmap interface of the adapter class, but the code that uses it won't even know, and the interface is refcounted, so the adapter will be freed as soon as it goes out of scope.
add a comment |
As David suggested, you could make an adapter class. This class could implement your interface and act (in most cases) as a simple passthrough to the actual bitmap class. The one for TBitmap could look like this (some methods left out for brevity):
type
TBitmapAdapter = class(TInterfacedObject, IBitmap)
private
FBitmap: TBitmap;
function GetWidth: Integer;
procedure SetWidth(Value: Integer);
// And everything else from IBitmap
public
constructor Create(ABitmap: TBitmap);
end;
function TBitmapAdapter.GetWidth: Integer;
begin
Result := FBitmap.Width;
end;
procedure TBitmapAdapter.SetWidth(Value: Integer);
begin
FBitmap.Width := Value;
end;
You could make a single class that handles both types, but that would require ifs in each of the methods. I would make two separate classes, so you can simply create a TBitmapAdapter or a TMyBitmapAdapter.
You can simply construct that class and pass it around as interface. If your TBmpUtility would accept an IBitmap, you can construct it like this:
TBmpUtility.Create(TBitmapAdapter.Create(Self))
You could indeed write class helpers for TBitmap and TMyBitmap to create the right wrapper for you. Again the TBitmap version:
type
TBitmapHelper = class helper for TBitmap
function AsIBitmap: IBitmap;
end;
function TBitmapHelper.AsIBitmap: IBitmap;
begin
Result := TBitmapAdapter.Create(Self);
end;
You can then simply use it like this:
TBmpUtility.Create(Bitmap.AsIBitmap)
Or maybe just pass it to the methods of the utility class, you can have one utility instance which can handle any bitmap:
BmpUtility.Fill(Bitmap.AsIBitmap);
The reference to IBitmap can be stored temporarily to prevent the adapter being created and destroyed on each call:
var b: IBitmap;
begin
b := Bitmap.AsIBitmap;
BmpUtility.SetHeight(b, 100);
BmpUtility.SetWidth(b, 100);
BmpUtility.Fill(b);
end;
The result of Bitmap.AsIBitmap is the IBitmap interface of the adapter class, but the code that uses it won't even know, and the interface is refcounted, so the adapter will be freed as soon as it goes out of scope.
As David suggested, you could make an adapter class. This class could implement your interface and act (in most cases) as a simple passthrough to the actual bitmap class. The one for TBitmap could look like this (some methods left out for brevity):
type
TBitmapAdapter = class(TInterfacedObject, IBitmap)
private
FBitmap: TBitmap;
function GetWidth: Integer;
procedure SetWidth(Value: Integer);
// And everything else from IBitmap
public
constructor Create(ABitmap: TBitmap);
end;
function TBitmapAdapter.GetWidth: Integer;
begin
Result := FBitmap.Width;
end;
procedure TBitmapAdapter.SetWidth(Value: Integer);
begin
FBitmap.Width := Value;
end;
You could make a single class that handles both types, but that would require ifs in each of the methods. I would make two separate classes, so you can simply create a TBitmapAdapter or a TMyBitmapAdapter.
You can simply construct that class and pass it around as interface. If your TBmpUtility would accept an IBitmap, you can construct it like this:
TBmpUtility.Create(TBitmapAdapter.Create(Self))
You could indeed write class helpers for TBitmap and TMyBitmap to create the right wrapper for you. Again the TBitmap version:
type
TBitmapHelper = class helper for TBitmap
function AsIBitmap: IBitmap;
end;
function TBitmapHelper.AsIBitmap: IBitmap;
begin
Result := TBitmapAdapter.Create(Self);
end;
You can then simply use it like this:
TBmpUtility.Create(Bitmap.AsIBitmap)
Or maybe just pass it to the methods of the utility class, you can have one utility instance which can handle any bitmap:
BmpUtility.Fill(Bitmap.AsIBitmap);
The reference to IBitmap can be stored temporarily to prevent the adapter being created and destroyed on each call:
var b: IBitmap;
begin
b := Bitmap.AsIBitmap;
BmpUtility.SetHeight(b, 100);
BmpUtility.SetWidth(b, 100);
BmpUtility.Fill(b);
end;
The result of Bitmap.AsIBitmap is the IBitmap interface of the adapter class, but the code that uses it won't even know, and the interface is refcounted, so the adapter will be freed as soon as it goes out of scope.
edited Nov 25 '18 at 14:53
answered Nov 25 '18 at 13:57
GolezTrolGolezTrol
99.1k10133176
99.1k10133176
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53463253%2fhow-to-use-a-generic-with-two-different-classes-but-one-of-them-from-the-vcl%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
1
I don't think generics are useful here. You need to create an adapter class that implements the interface by forwarding calls to the TBitmap instance.
– David Heffernan
Nov 25 '18 at 2:53
1
Related: Invoke method on generic type?
– Remy Lebeau
Nov 25 '18 at 6:41