# Inheritance question

**URL:** <https://www.ruby-forum.com/t/inheritance-question/123727>\
**Category:** IronRuby\
**Created:** [December 20, 2007, 1:56am UTC](https://www.ruby-forum.com/t/inheritance-question/123727 "2007-12-20T01:56:45Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![Terence\_L](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/439d5e/32.png) [@Terence\_L](https://www.ruby-forum.com/u/Terence_L)\
**Post date:** [December 20, 2007, 1:56am UTC](https://www.ruby-forum.com/t/inheritance-question/123727/1 "2007-12-20T01:56:45Z")

</div>

Hello list,

Two things:

1. 

I’ve got a question regarding inheritance and abstract base classes  
that I’m hoping somebody can give me some help with. I’m trying to  
make some socket-based stuff work based on the descriptions in the  
pickaxe book which states that “BasicSocket is an abstract base class  
for other socket classes”. It also says “IPSocket is a base class for  
sockets using IP as their transport. TCPSocket and UDPSocket are based  
on this class”. I’ve successfully implemented the class method on  
IPSocket, but when I wanted to make TCPSocket.addr and  
TCPSocket.peeraddr work (both methods should be inherited from  
IPSocket), I had to write this functionality directly into my  
TCPSocketOps class - if I put it into IPSocketOps the methods are not  
found. Now I have to implement UDPSocket.addr and UDPSocket.peeraddr  
and I don’t want to duplicate the logic in the methods (besides, then  
it’s not really inheritance is it?).

To make my question clearer, I’ll paste the code below. The methods in  
question are marked with TODO’s - they sit in the TCPSocketOps class  
which is marked with RubyClass(“TCPSocket”) instead of inheriting the  
methods from RubyClass(“IPSocket”) and I’m questioning whether there  
is another way to do this or if that’s really correct. Thanks for your  
time. Also, the exception I get when I move the methods to IPSocketOps  
is:

System.MissingMethodException: undefined local variable or method  
`addr’ for  
#Ruby::BuiltIns::TCPSocket:0x0000058:Ruby.Builtins.RubyClass  
at Ruby.Builtins.Kernel.MethodMissing(CodeContext context, Object  
self, BlockParam block, SymbolId name, Object[] args) in  
D:\Projects\IronRuby\trunk\src\IronRuby.Libraries\Builtins\Kernel.cs:line  
227

using System;  
using System.Collections.Generic;  
using System.Text;  
using Ruby.Runtime;  
using System.Net.Sockets;  
using Ruby.Builtins;  
using Microsoft.Scripting;  
using [System.Net](http://System.Net);

namespace Ruby.BuiltIns {

```
[RubyClass("IPSocket")]
public static class IPSocketOps {
    [RubyMethod("getaddress", RubyMethodAttributes.PublicSingleton)]
    public static string GetAddress(object self, MutableString 

```

hostName) {  
return  
System.Net.Dns.GetHostEntry(hostName).AddressList[0].ToString();  
}  
}

```
[RubyClass("TCPSocket", Inherits = typeof(Object), Extends =

```

typeof(IPSocket))]  
public static class TCPSocketOps {

```
    [RubyMethod("gethostbyname", 

```

RubyMethodAttributes.PublicSingleton)]  
public static object GetHostByName(object self, MutableString  
hostName) {  
RubyArray result = ArrayOps.CreateArray();  
System.Net.IPHostEntry hostEntry =  
System.Net.Dns.GetHostEntry(hostName);  
result.Add(hostName);  
result.Add(ArrayOps.CreateArray(hostEntry.Aliases.Length));  
foreach (string alias in hostEntry.Aliases)  
{  
((RubyArray)result[1]).Add(new MutableString(alias));  
}  
result.Add((int)hostEntry.AddressList[0].AddressFamily);  
result.Add(new  
MutableString(hostEntry.AddressList[0].ToString()));  
return result;  
}

```
    [RubyMethod("new", RubyMethodAttributes.PublicSingleton)]
    public static TCPSocket/*!*/ CreateSocket(CodeContext/*!*/

```

context, object self, BlockParam/_!_/ startRoutine, [NotNull]params  
object[]/_!_/ args) {  
return new TCPSocket(args[0], args[1]);  
}

```
    [RubyMethod("open", RubyMethodAttributes.PublicSingleton)]
    public static TCPSocket/*!*/

```

CreateSocketAlias\_Open(CodeContext/_!_/ context, object self,  
BlockParam/_!_/ startRoutine, [NotNull]params object[]/_!_/ args) {  
return CreateSocket(context, self, startRoutine, args);  
}

```
    // TODO: This should move into IPSocketOps - How does

```

inheritance work in IronRuby?  
[RubyMethod(“addr”, RubyMethodAttributes.PublicInstance)]  
public static object GetAddress(IPSocket self) {  
return GetAddressFromEndPoint(self.socket.LocalEndPoint);  
}

```
    // TODO: This should move into IPSocketOps - How does

```

inheritance work in IronRuby?  
[RubyMethod(“peeraddr”, RubyMethodAttributes.PublicInstance)]  
public static object GetPeerAddress(IPSocket self) {  
return GetAddressFromEndPoint(self.socket.RemoteEndPoint);  
}

```
    public static object GetAddressFromEndPoint(EndPoint endPoint) {
        RubyArray result = ArrayOps.CreateArray();
        IPEndPoint ep = (IPEndPoint)endPoint;
        MutableString addressFamily = null;
        switch (ep.AddressFamily) {
            // Surely this already exists somewhere in System.Net?

```

Couldn’t find it though…  
case AddressFamily.InterNetwork:  
addressFamily = new MutableString(“AF\_INET”);  
break;  
default:  
throw new NotImplementedException();  
}

```
        result.Add(addressFamily);
        result.Add(ep.Port);
        result.Add(new

```

MutableString(System.Net.Dns.GetHostEntry(ep.Address).HostName));  
result.Add(new MutableString(ep.Address.ToString()));  
return result;  
}  
}

```
public class IPSocket {
    internal Socket socket;
}

public class TCPSocket : IPSocket {
    public TCPSocket(object hostname, object port) {
        socket = new Socket(AddressFamily.InterNetwork,

```

SocketType.Stream, ProtocolType.Tcp);

```
        int portNumber = 0;

        if (port is int) {
            portNumber = (int)port;
        }
        else if (port is MutableString) {
            switch ((MutableString)port) {
                case "ftp":
                    portNumber = 21;
                    break;
                case "http":
                    portNumber = 80;
                    break;
                default:
                    throw new NotImplementedException();
            }
        }

        socket.Connect((MutableString)hostname, portNumber);
    }
}

```

}

---

<div class="post-metadata">

**Author:** ![Terence\_L](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/439d5e/32.png) [@Terence\_L](https://www.ruby-forum.com/u/Terence_L)\
**Post date:** [December 20, 2007, 2:00am UTC](https://www.ruby-forum.com/t/inheritance-question/123727/2 "2007-12-20T02:00:50Z")

</div>

Apologies - I have an itchy send finger. The other issue is that I  
think the file CalcOps.cs is in the repository, but not included in  
the project - so the code refuses to build cleanly on the latest  
revision. I worked around this by manually adding the file to the  
correct project in visual studio.

Thanks for your time

Terence

---

<div class="post-metadata">

**Author:** ![Terence\_L](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/439d5e/32.png) [@Terence\_L](https://www.ruby-forum.com/u/Terence_L)\
**Post date:** [December 20, 2007, 2:06am UTC](https://www.ruby-forum.com/t/inheritance-question/123727/3 "2007-12-20T02:06:06Z")

</div>

Terence L.:

> Apologies - I have an itchy send finger. The other issue is that I  
> think the file CalcOps.cs is in the repository, but not included in  
> the project - so the code refuses to build cleanly on the latest  
> revision. I worked around this by manually adding the file to the  
> correct project in visual studio.

I just removed this file from SVN. It was an artifact of a demo that I  
did at RubyConf …

-John

---

<div class="post-metadata">

**Author:** ![Terence\_L](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/439d5e/32.png) [@Terence\_L](https://www.ruby-forum.com/u/Terence_L)\
**Post date:** [December 20, 2007, 5:39pm UTC](https://www.ruby-forum.com/t/inheritance-question/123727/4 "2007-12-20T17:39:03Z")

</div>

Alternatively, you may prefer not to wrap the .NET type and to extend  
System.Net.Sockets.Socket into the DLR directly. This is inline with  
the  
extension of other .NET classes such as Int32 to Fixnum and  
MutableString to  
MutableString.  
The following code demonstrates how you could do this…

using System;  
using System.Collections.Generic;  
using System.Text;  
using Ruby.Runtime;  
using System.Net.Sockets;  
using Ruby.Builtins;  
using Microsoft.Scripting;  
using [System.Net](http://System.Net);

namespace Ruby.BuiltIns {

```
[RubyClass("IPSocket", Extends=typeof(Socket))]
public static class IPSocketOps {
    [RubyMethod("getaddress", RubyMethodAttributes.PublicSingleton)]
    public static string GetAddress(RubyClass klass, MutableString

```

hostName) {  
return  
System.Net.Dns.GetHostEntry(hostName).AddressList[0].ToString();  
}

```
   [RubyMethod("addr", RubyMethodAttributes.PublicInstance)]
    public static object GetAddress(Socket self) {
        return GetAddressFromEndPoint(self.LocalEndPoint);
    }

    [RubyMethod("peeraddr", RubyMethodAttributes.PublicInstance)]
    public static object GetPeerAddress(Socket self) {
        return GetAddressFromEndPoint(self.RemoteEndPoint);
    }

    public static object GetAddressFromEndPoint(EndPoint endPoint) {
        RubyArray result = ArrayOps.CreateArray();
        IPEndPoint ep = (IPEndPoint)endPoint;
        MutableString addressFamily = null;
        switch (ep.AddressFamily) {
            // Surely this already exists somewhere in System.Net?

```

Couldn’t find it though…  
case AddressFamily.InterNetwork:  
addressFamily = new MutableString(“AF\_INET”);  
break;  
default:  
throw new NotImplementedException();  
}

```
        result.Add(addressFamily);
        result.Add(ep.Port);
        result.Add(new

```

MutableString(System.Net.Dns.GetHostEntry(ep.Address).HostName));  
result.Add(new MutableString(ep.Address.ToString()));  
return result;  
}  
}

```
[RubyClass("TCPSocket", Inherits=typeof(IPSocketOps))]
public static class TCPSocket : Object{

    [RubyMethod("gethostbyname", 

```

RubyMethodAttributes.PublicSingleton)]  
public static object GetHostByName(object self, MutableString  
hostName) {  
RubyArray result = ArrayOps.CreateArray();  
System.Net.IPHostEntry hostEntry =  
System.Net.Dns.GetHostEntry(hostName);  
result.Add(hostName);  
result.Add(ArrayOps.CreateArray(hostEntry.Aliases.Length));  
foreach (string alias in hostEntry.Aliases) {  
((RubyArray)result[1]).Add(new MutableString(alias));  
}  
result.Add((int)hostEntry.AddressList[0].AddressFamily);  
result.Add(new  
MutableString(hostEntry.AddressList[0].ToString()));  
return result;  
}

```
    [RubyMethod("new", RubyMethodAttributes.PublicSingleton)]
    public static Socket/*!*/ CreateSocket(CodeContext/*!*/ context,

```

object self, BlockParam/_!_/ startRoutine, [NotNull]params object[]/_!_/  
args) {  
object hostname = args[0];  
object port = args[1];  
Socket socket = new Socket(AddressFamily.InterNetwork,  
SocketType.Stream, ProtocolType.Tcp);

```
        int portNumber = 0;

        if (port is int) {
            portNumber = (int)port;
        } else if (port is MutableString) {
            switch ((MutableString)port) {
                case "ftp":
                    portNumber = 21;
                    break;
                case "http":
                    portNumber = 80;
                    break;
                default:
                    throw new NotImplementedException();
            }
        }

        socket.Connect((MutableString)hostname, portNumber);
        return socket;
    }

    [RubyMethod("open", RubyMethodAttributes.PublicSingleton)]
    public static Socket/*!*/ 

```

CreateSocketAlias\_Open(CodeContext/_!_/  
context, object self, BlockParam/_!_/ startRoutine, [NotNull]params  
object[]/_!_/ args) {  
return CreateSocket(context, self, startRoutine, args);  
}

```
}

```

}

---

<div class="post-metadata">

**Author:** ![Terence\_L](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/439d5e/32.png) [@Terence\_L](https://www.ruby-forum.com/u/Terence_L)\
**Post date:** [December 20, 2007, 5:30pm UTC](https://www.ruby-forum.com/t/inheritance-question/123727/5 "2007-12-20T17:30:51Z")

</div>

I think you are getting too carried away with the various conventions  
around  
extending .NET types for use by the DLR.  
If you are simply wanting to wrap the underlying  
System.Net.Sockets.Socket  
class (rather than extend it) then you don’t need to have both IPSocket  
and  
IPSocketOps classes and so on. It is enough to have an abstract  
non-static  
class IPSocket and a concrete class TCPSocket, which derives directly  
from  
IPSocket. Everything then works. See the code below.

using System;  
using System.Collections.Generic;  
using System.Text;  
using Ruby.Runtime;  
using System.Net.Sockets;  
using Ruby.Builtins;  
using Microsoft.Scripting;  
using [System.Net](http://System.Net);

namespace Ruby.BuiltIns {

```
[RubyClass("IPSocket")]
public abstract class IPSocket {
    internal Socket socket;

    [RubyMethod("getaddress", RubyMethodAttributes.PublicSingleton)]
    public static string GetAddress(object self, MutableString 

```

hostName)  
{  
return  
System.Net.Dns.GetHostEntry(hostName).AddressList[0].ToString();  
}

```
   [RubyMethod("addr", RubyMethodAttributes.PublicInstance)]
    public static object GetAddress(IPSocket self) {
        return GetAddressFromEndPoint(self.socket.LocalEndPoint);
    }

   [RubyMethod("peeraddr", RubyMethodAttributes.PublicInstance)]
    public static object GetPeerAddress(IPSocket self) {
        return GetAddressFromEndPoint(self.socket.RemoteEndPoint);
    }

    public static object GetAddressFromEndPoint(EndPoint endPoint) {
        RubyArray result = ArrayOps.CreateArray();
        IPEndPoint ep = (IPEndPoint)endPoint;
        MutableString addressFamily = null;
        switch (ep.AddressFamily) {
            // Surely this already exists somewhere in System.Net?

```

Couldn’t find it though…  
case AddressFamily.InterNetwork:  
addressFamily = new MutableString(“AF\_INET”);  
break;  
default:  
throw new NotImplementedException();  
}

```
        result.Add(addressFamily);
        result.Add(ep.Port);
        result.Add(new

```

MutableString(System.Net.Dns.GetHostEntry(ep.Address).HostName));  
result.Add(new MutableString(ep.Address.ToString()));  
return result;  
}  
}

```
[RubyClass("TCPSocket")]
public class TCPSocket : IPSocket {

    [RubyMethod("gethostbyname", 

```

RubyMethodAttributes.PublicSingleton)]  
public static object GetHostByName(object self, MutableString  
hostName) {  
RubyArray result = ArrayOps.CreateArray();  
System.Net.IPHostEntry hostEntry =  
System.Net.Dns.GetHostEntry(hostName);  
result.Add(hostName);  
result.Add(ArrayOps.CreateArray(hostEntry.Aliases.Length));  
foreach (string alias in hostEntry.Aliases) {  
((RubyArray)result[1]).Add(new MutableString(alias));  
}  
result.Add((int)hostEntry.AddressList[0].AddressFamily);  
result.Add(new  
MutableString(hostEntry.AddressList[0].ToString()));  
return result;  
}

```
    [RubyMethod("new", RubyMethodAttributes.PublicSingleton)]
    public static TCPSocket/*!*/ CreateSocket(CodeContext/*!*/ 

```

context,  
object self, BlockParam/_!_/ startRoutine, [NotNull]params object[]/_!_/  
args) {  
return new TCPSocket(args[0], args[1]);  
}

```
    [RubyMethod("open", RubyMethodAttributes.PublicSingleton)]
    public static TCPSocket/*!*/ 

```

CreateSocketAlias\_Open(CodeContext/_!_/  
context, object self, BlockParam/_!_/ startRoutine, [NotNull]params  
object[]/_!_/ args) {  
return CreateSocket(context, self, startRoutine, args);  
}

```
    public TCPSocket(object hostname, object port) {
        socket = new Socket(AddressFamily.InterNetwork,

```

SocketType.Stream, ProtocolType.Tcp);

```
        int portNumber = 0;

        if (port is int) {
            portNumber = (int)port;
        } else if (port is MutableString) {
            switch ((MutableString)port) {
                case "ftp":
                    portNumber = 21;
                    break;
                case "http":
                    portNumber = 80;
                    break;
                default:
                    throw new NotImplementedException();
            }
        }

        socket.Connect((MutableString)hostname, portNumber);
    }
}

```

}
