Extend system class with new constructors

I’m trying to port Delphi code that has lots of calls like this:

raise EMyException.CreateRes(@SomeResourceString);
raise EMyException.CreateResFmt(@SomeResourceString, [Parameter1]);
raise EMyException.CreateFmt("Some string", [Parameter 1]);

Those constructors are not available in the Exception class, so I tried writing an extension class for it, like so:

type
  PResStringRec = ^String;

  ExceptionExtension = public extension class(Exception)
  public
    method CreateRes(AResourceString: PResStringRec): Exception; static;
    begin
      self.GetType().GetConstructor(typeOf(String)).Invoke(new Object[](AResourceString^));
    end;
  end;

Alas, this does not compile as it tells me I cannot access non static member GetType. The idea being this construct was to create an instance of the real class, not just a Exception object.

Would you have any suggestions?

Try to use typeOf (Exception);

rightfully so. I don’t think this is feasible, as static method are not polymorphic, so the method has no access to knowing that the “actual type” you called it on is.

Your best option would be declaring an actual constructor (which you can do in extensions, too), but I;'m not even sure that that would work. This might need virtual constructors (which we support in combination with “class of” class references, or it might even need additional compiler support to get right at all.

I’ll need to defer this to Carlo to answer next week.

I assume Olivier wants to create a more concrete type, ie MySpecialException.CreateRes should create a MySpecialException instance. Else he would’t need the type magic at all and could just do new Exception in there.

Exactly, I want it to be the real Exception type because it needs to be catchable properly.

Well, I tried it too, but somehow it did not seem to work, mostly because I believe the constructor is not called “Create” (or the name of its class).

My gut feeling here this that this will need additional compiler support, to work in extensions (if feasible at all). I’ll log an issue and discuss it with Carlo next week,

1 Like

We do support .Create* constrictor names, int Delphi Compatibility Mode, but I mist admit i’m fuzzy on what exactly the scope of that is, as I never used it myself.

Thanks, logged as bugs://84636

bugs://84636 got closed with status fixed.

So this will work:

Note the extension, and inline. both are important here. Inline makes it remember the real type. Without inline it would use Exception for both.

Awesome, thanks for the insight

Let me know if this solves it for you, sufficiently.

No need to assign the result?
When I wrote it here, I had this:

class method CreateRes(AResourceString: PResStringRec): Exception; static; inline;
begin
  result := typeOf(self).GetConstructor([typeOf(String)]).Invoke([AResourceString^]); 
end;

But it rightfully complained that Object cannot be assigned to Exception. Before casting I was wondering why result is not assigned in your example. Is it because of the inline keyword?

No that was actually just a mistake of me :slight_smile: It should assign the result yes. I didn’t spot it because I didn’t actually use the result.

1 Like

@ck
This is awesome but it only works with .NET, not Island?

It should just work for everything, it’s an inline method. Obviously you need different (reflection) code for Java and Island.

I am scratching my head. I cannot find out the equivalent Island code to invoke constructor.

Good question; I don’t think we have this exposed for constructors with parameters yet.

There’s:

    method Instantiate<T>: Object; where T is ILifetimeStrategy<T>; // Creates a new instance of this type and calls the default constructor, fails if none is present!
    begin
      var lCtor: MethodInfo := Methods.FirstOrDefault(a -> (MethodFlags.Constructor in a.Flags) and (not a.Arguments.Any) and (not a.IsStatic));
      if lCtor = nil then raise new Exception('No default constructor could be found on type '+Name);
      var lRealCtor := CtorHelper(lCtor.Pointer);
      if lRealCtor = nil then raise new Exception('No default constructor could be found!');
      result := InternalCalls.Cast<Object>(T.New(fValue, SizeOfType));
      lRealCtor(result);
    end;


    method Instantiate: Object; // Creates a new instance of this type and calls the default constructor, fails if none is present!
    begin
      exit Instantiate<DefaultGC>();
    end;

On type, which does this for parameterless constructors.

I think

var aTypeDef := typeof(self);
var lMethodInfoForConstructor := aTypeDef.Methods.FirstOrDefault(a -> (MethodFlags.Constructor in a.Flags) and (a.Arguments.Count = 1) and (a.Arguments[0].TypeCode = TypeCodes.String) and (not a.IsStatic));
lMethodInfoForConstructor.Invoke(DefaultGC.New(aTypeDef.RTTI, aTypeDef.SizeOfType), [arguments])

should work.

I would put the typeof() in the inline method, and the complex logic in a non inlined method, for speed purposes.

typeOf(self) is used in the above code. According to Oxygene document, copied below:

  • self inside a static member is identical to typeOf (self) in an instance method.

Based on the above document, the following two lines should be identical:

result := typeOf(self).GetConstructor([typeOf(String)]).Invoke([AResourceString^]);

should be identical to

result := self.GetConstructor([typeOf(String)]).Invoke([AResourceString^]);

However, the compiler complains about the second: (E43) No static member “GetConstructor” on type "Exception"

Any headsup?

@ck should we maybe expose this an s proper “syntax” so this can be achieved w/o reflection hacks?