[Ironruby-core] Inheritance in IronRuby - possibly a bug or two?

Charles Strahan charles.c.strahan at gmail.com
Fri Sep 24 05:04:38 EDT 2010


Ah, the semantics do make sense now.

In that case, perhaps there's one more solution:

class PersonShim < Example1::Person
  def self.new(first, last)
    Example1::Person.new(first, last)
  end
end

class Person2 < PersonShim
  def initialize(first, last)
    super(first, last)
  end
end

Person2.new(""Foo"", ""Bar"")



In my case, I need to make sure that the C# types that I expose to IronRuby
behave almost identically to what you would expect with a pure Ruby
implementation. If you remember back when I was talking about my game engine
clone, I need to make sure that I don't break user's scripts - so I need to
preserve the "expected" semantics of `initialize`. What I could do is end my
C# classes with "Impl", and write a corresponding Ruby class that invokes
the correct constructor in `new` (ex: SpriteSubclass -> Sprite ->
SpriteImpl). Mind giving me a sanity check on this, Tomas?

I think this should fix the problem I was having with subclassing my Sprite
class - which get's me one step closer to releasing this beast!


Thank you, Tomas, that was a very thorough explanation.

-Charles


On Fri, Sep 24, 2010 at 2:38 AM, Tomas Matousek <
Tomas.Matousek at microsoft.com> wrote:

>  This behavior is actually by design.
>
>
>
> The error message says: “… define Person2#new singleton method instead of
> Person2#initialize” like so:
>
>
>
> class Person2 < Example1::Person
>
>   def self.new(first, last)
>
>     super(first, last)
>
>   end
>
> end
>
>
>
> person = Person2.new("Foo", "Bar")
>
> p person.first_name, person.last_name
>
>
>
> We do pick up the superclass’s constructors if you don’t specify no
> initialize method, so this works too:
>
>
>
> class Person2 < Person
>
> end
>
>
>
> person = Person2.new("Foo", "Bar")
>
> p person.first_name, person.last_name
>
>
>
> The reason why this doesn’t work when you define initialize is to make you
> aware of the fact that your initialize method doesn’t do what you might
> expect, that is it doesn’t call the constructor of the superclass.
>
>
>
> If you define a default ctor in the superclass and you have initialize
> method in the subclass, the constructor gets invoked to create the object
> and initialize is called to initialize it. This patter is kind of close to
> Ruby semantics.
>
>
>
> So you basically have 3 options:
>
> -          Do not define new nor initialize => the ctors from base class
> are available for construction of the subclass
>
> -          Define singleton new => you can choose which base ctor is
> called.
>
> -          The base class has a default ctor and the subclass defines
> initialize method => the default ctor is always used for object construction
> and the initialize is called with the arguments given to “new”.
>
>
>
> The reason why we chose this design is due to difference between Ruby and
> CLR initialization semantics. CLR classes don’t separate allocation
> (“allocate”) from initialization (“initialize”) like Ruby does. CLR has just
> constructors (which kind of corresponds to Ruby factory method “new”).
> Constructors combine allocation and initialization. The problem with mapping
> initialize to CLR constructors is that it operates on “self” that is already
> allocated before you can do anything (like call super):
>
>
>
>   def initialize(first, last)
>
>    p self.first_name                 # self is already an instance of
> Person2 here, so we must have called some constructor already (the default
> one if available)
>
>     super(first, last)                   # what should this do? We can’t
> call the constructor again… it’s too late.
>
>   end
>
>
>
>
>
> As for what super(first, last) does in your code … it calls
> “Object#initialize”, which in Ruby 1.9.2 has *args parameters and does
> nothing:
>
>
>
> >>> class X; end
>
> => nil
>
> >>> init = X.instance_method(:initialize)
>
> => #<UnboundMethod: X(Object)#initialize>
>
> >>> init.parameters
>
> => [[:rest]]
>
> >>> X.new.send(:initialize, 1,2,3,4,5)
>
> => #<X:0x0000056>
>
>
>
> Tomas
>
>
>
> *From:* ironruby-core-bounces at rubyforge.org [mailto:
> ironruby-core-bounces at rubyforge.org] *On Behalf Of *Charles Strahan
> *Sent:* Thursday, September 23, 2010 11:40 PM
> *To:* ironruby-core at rubyforge.org
> *Subject:* [Ironruby-core] Inheritance in IronRuby - possibly a bug or
> two?
>
>
>
> I have a couple questions about deriving from C# class from IronRuby. For
> context, here's a code example that I will refer to here in a bit:
>
>
>
> ========================================
>
>
>
> using System;
>
> using System.Reflection;
>
> using IronRuby;
>
> using IronRuby.Runtime;
>
> using Microsoft.Scripting.Hosting.Providers;
>
>
>
> namespace Example1
>
> {
>
>     public class Person
>
>     {
>
>         public string FirstName { get; set; }
>
>         public string LastName { get; set; }
>
>
>
>         public Person(string firstName, string lastName)
>
>         {
>
>             FirstName = firstName;
>
>             LastName = lastName;
>
>         }
>
>     }
>
>
>
>     class Program
>
>     {
>
>         private static readonly string _rubyScript = @"
>
>
>
> class Person2 < Example1::Person
>
>   def initialize(first, last)
>
>     super(first, last)
>
>   end
>
> end
>
>
>
> Person2.new(""Foo"", ""Bar"")
>
>
>
> ";
>
>
>
>         static void Main(string[] args)
>
>         {
>
>             var runtime = Ruby.CreateRuntime();
>
>             var engine = runtime.GetEngine("rb");
>
>             var context =
> (RubyContext)HostingHelpers.GetLanguageContext(engine);
>
>             var scope = engine.CreateScope();
>
>             runtime.LoadAssembly(typeof(Program).Assembly);
>
>
>
>             engine.Execute(_rubyScript, scope);
>
>
>
>             Console.WriteLine(". . .");
>
>             Console.ReadKey(true);
>
>         }
>
>     }
>
> }
>
>
>
>
>
> ========================================
>
>
>
>
>
> If you run that code, you'll get the following:
>
>
>
> InvalidOperationException: can't allocate class `Person2' that derives from
> type `Example1::Person' with no default constructor; define Person2#new
> singleton method instead of Person2#initialize
>
>
>
> Is this a bug, or is this intended behavior?  If this is intentional, then
> I think there's still a different problem: try adding this default
> constructor and then run the code:
>
>
>
>         public Person()
>
>         {
>
>             Console.WriteLine("Uhmmm... what did IronRuby do with
> `super(first, last)`?");
>
>         }
>
>
>
> So, the `super(first, last)` still get's executed... but what did it do? It
> obviously didn't forward those arguments to the non-default constructor...
>
>
>
> So, I think that means we have one, or possibly two, bugs.
>
>
>
> Back to the first question: Wouldn't it be possible to determine the
> correct constructor to invoke based on the arguments, thus avoiding the
> exception? I would imagine that the generated/emitted subclass could contain
> all of the same constructors that the base type has, just passing the
> arguments on to the base class's corresponding constructor (I hope that made
> sense - sorta tricky to word that correctly).
>
>
>
>
>
> By the way, how should I specify that I _don't_ want the Ruby code to be
> interpreted? I noticed that the debugger broke
> in Microsoft.Scripting.Interpreter.Interpreter... maybe that could be part
> of the problem.
>
>
>
> Cheers,
>
> -Charles
>
> _______________________________________________
> Ironruby-core mailing list
> Ironruby-core at rubyforge.org
> http://rubyforge.org/mailman/listinfo/ironruby-core
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://rubyforge.org/pipermail/ironruby-core/attachments/20100924/dc101855/attachment.html>


More information about the Ironruby-core mailing list