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-boun...@rubyforge.org 
[mailto:ironruby-core-boun...@rubyforge.org] On Behalf Of Charles Strahan
Sent: Thursday, September 23, 2010 11:40 PM
To: ironruby-core@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@rubyforge.org
http://rubyforge.org/mailman/listinfo/ironruby-core

Reply via email to