C# Limitation 1: no Virtual Static Methods
After some months of intensive C# learning and development, I've run into few limitations - things that Anders and others must have intentionally chosen not to do. For example, "Virtual Static Methods" - sounds all academic right ? How about this :
I am writing an object messaging library, and I want to have the objects themselves recognise their own binary stream, so that when deserializing an object from binary, I can run through a list of registered message classes, call a virtual function, and if it returns true, that is the correct class to use, and so I would call its Constructor to create an instance, and then read in its properties.
C# has two barriers to this that Delphi handles fine.
1) In C#, static functions cannot be virtual. In Delphi you would declare the message base class with a static virtual method
Recognise(TStream aStream): boolean
then override it with each specific type of message. In C# a method can be static or virtual, but not both. To use a virtual instance (non-static) method I must have already created the object, but I want to use the Recognise method to determine whether to instantiate that class in the first place ! You could I suppose implement the "prototype" design pattern, where you keep a list of objects (rather than classes) available for cloning.
2) In C#, constructors cannot be virtual either, so when I know what class to create, I can't just create it polymorphically.
Thankfully C# does have a very powerful equivalent to Delphi's RTTI (Run Time Type Information) known as Reflection and we can solve these problems with it.I solved the first issue with a static property called MessageID that must be declared in every message class (non-virtually). The message creating code keeps a list of registered classes (more on this later), reads a certain byte from the incoming stream and looks for a match with the values of MessageID in the list. It now has the class required and must create it (issue 2).
To do this, it again uses reflection in a method (see CreateFromClass() below) that will create an instance of any class having a constructor with no parameters.
AliasClassFactory contains the code for registering and creating the classes. Message classes can be specifically registered with their ID as a string (the string can of course be a number).
To get the MessageID property value from any class use this utility method :
public static object GetStaticProperty(Type aType, string aProperty) {
return aType.InvokeMember(aProperty, BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy, null, null, null);
}
Whenever a method has a parameter of type Type like aType above (did you get that?), you can call it like this with a class :
int id = (int) GetStaticProperty(typeof(MyMessage), "MessageID")
or like this with an instance of a class
int id = (int) GetStaticProperty(myMessage.GetType(), "MessageID")
I am writing an object messaging library, and I want to have the objects themselves recognise their own binary stream, so that when deserializing an object from binary, I can run through a list of registered message classes, call a virtual function, and if it returns true, that is the correct class to use, and so I would call its Constructor to create an instance, and then read in its properties.
C# has two barriers to this that Delphi handles fine.
1) In C#, static functions cannot be virtual. In Delphi you would declare the message base class with a static virtual method
Recognise(TStream aStream): boolean
then override it with each specific type of message. In C# a method can be static or virtual, but not both. To use a virtual instance (non-static) method I must have already created the object, but I want to use the Recognise method to determine whether to instantiate that class in the first place ! You could I suppose implement the "prototype" design pattern, where you keep a list of objects (rather than classes) available for cloning.
2) In C#, constructors cannot be virtual either, so when I know what class to create, I can't just create it polymorphically.
Thankfully C# does have a very powerful equivalent to Delphi's RTTI (Run Time Type Information) known as Reflection and we can solve these problems with it.I solved the first issue with a static property called MessageID that must be declared in every message class (non-virtually). The message creating code keeps a list of registered classes (more on this later), reads a certain byte from the incoming stream and looks for a match with the values of MessageID in the list. It now has the class required and must create it (issue 2).
To do this, it again uses reflection in a method (see CreateFromClass() below) that will create an instance of any class having a constructor with no parameters.
AliasClassFactory contains the code for registering and creating the classes. Message classes can be specifically registered with their ID as a string (the string can of course be a number).
To get the MessageID property value from any class use this utility method :
public static object GetStaticProperty(Type aType, string aProperty) {
return aType.InvokeMember(aProperty, BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy, null, null, null);
}
Whenever a method has a parameter of type Type like aType above (did you get that?), you can call it like this with a class :
int id = (int) GetStaticProperty(typeof(MyMessage), "MessageID")
or like this with an instance of a class
int id = (int) GetStaticProperty(myMessage.GetType(), "MessageID")
using System;
using System.Reflection;
using System.Collections;
using System.Diagnostics;
namespace myspace {
/// Register classes with aliases, then create them from the alias.
public class AliasClassFactory {
ArrayList list;
Type baseClass;
/// helper class used for each item in the list of classes
protected internal class AliasClassItem {
Type vClass;
string vAlias;
public Type Class {
get {
return vClass;
}
}
public string Alias {
get {
return vAlias;
}
}
public AliasClassItem(Type aClass, string aAlias) : base() {
this.vClass = aClass;
this.vAlias = aAlias;
}
}
///Create factory. Registered classes must descend from base class given here
///(can be object)
public AliasClassFactory(Type aBaseClass) : base () {
baseClass = aBaseClass;
list = new ArrayList();
}
///Classes must be registered with an alias before being created by the factory
public void Register(Type aClass, string aAlias) {
if (!aClass.IsSubclassOf(baseClass))
throw new Exception("Class must be a subclass of "+baseClass.FullName);
Type vClass = ClassFromAlias(aAlias);
if (vClass != null)
throw new Exception("Alias already registed with class "+vClass.FullName);
list.Add(new AliasClassItem(aClass,aAlias));
Trace.WriteLine("Class "+aClass.FullName+" was registered with alias :"+aAlias);
}
///Method for creating an object from a class. Requires constructor with no arguments.
///This limitation could be removed later.
public static object CreateFromClass(Type aClass) {
ConstructorInfo constructorInfo = aClass.GetConstructor(
BindingFlags.Instance | BindingFlags.Public,
null,
CallingConventions.HasThis,
new Type[0] {},//aPars,
null
);
if(constructorInfo != null)
return constructorInfo.Invoke(null);
else
throw new Exception("Constructor not found");
}
///Returns the class given an alias
public Type ClassFromAlias(string aAlias) {
foreach (object obj in list) {
if ( String.Compare( ((AliasClassItem) obj).Alias, aAlias, true)==0 ) {
return ((AliasClassItem) obj).Class;
}
}
return null;
}
///Creates a class given an alias.
public object CreateFromAlias(string aAlias) {
Type vClass = ClassFromAlias(aAlias);
if (vClass==null)
throw new Exception("Class not registered");
return CreateFromClass(vClass);
}
}
}

2 Comments:
Why would you want any "class virtual" method at all? I can think on several nice to do with virtual constructors, and with "class" methods, but I still have to find any worth to "class virtual" methods. I really think that, if you ever need that functionality, then you have gone too far and what you really need is a well designed class factory framework.
... of course, that's my humble opinion, just thinking aloud.
Virtual static methods (or properties) allow you to do polymorphism with the class like you would with an object. Say for a plug-in architecture, you can define a standard base class for the plugins with static virtual methods for information such as an icon or name, and query them polymorphically (calling them as if the class was the base class) without (or before) actually creating the plugin.
I guess C# could achieve this with attributes...
I think the reason for this difference is to do with classes in C# being (I think) an instance of the type "class" with properties indicating its name etc.
(haven't investigated this fully)
In Delphi, a class truly is a descendant of its ancestor class, just as an instance of a class is a descendant of its ancestor class (that sounds like complete dribble!).
Post a Comment
<< Home