Board index » delphi » No virtual class methods in dotnet

No virtual class methods in dotnet


2006-08-24 03:16:58 PM
delphi254
I found virtual class methods in Delphi very useful for doing stuff like
this
TSignalReceiver = class.......
public
function MayAcceptSignal(Signal: TSignal); virtual;
procedure AcceptSignal(Signal: TSignal); virtual;
end;
TSignal = class.....
public
class function MayExecute(SignalReceiver: TSignalReceiver): boolean;
virtual;
procedure Execute(SignalReceiver: TSignalReceiver); virtual;
end;
To get a list of valid signals for my receiver I can iterate a list of class
types descended from TSignal and check Signal.MayExecute(Receiver), if it
returns True then I can display the signal as a valid type to send to my
receiver instance.
In dotnet there are no virtual class methods so I can not use the same code.
My options as I see them are:
1) Always have an instance of each Signal class created so that I can use
non-static methods
PRO: It would work exactly the same
CON: If I am streaming all live objects (which I intend to) this would
increase the size of the stream + take more time
2) Have a single class that decides for me
PRO: It would use a lot less space
CON: I'd have to update this single class every time I added a new
signal class
CON: It just doesn't seem like "real OOP" :-)
3) Each signal class has some kind of "manager" class (for lack of a better
word) that is responsible for approving/rejecting.
PRO: It would work properly
PRO: It would be smaller to stream than the signal itself (which may have
many properties)
CON: I have to write two classes every time I add a signal
CON: It is still more to stream
CON: In my SignalReceiver.AcceptSignal by default I'd have in
MayAcceptSignal
Can anyone think of a better solution? Virtual class methods would be so
nice but as I am writing in C# these are not an option I have.
--
Pete
Blessed are the geek, for they shall public class GeekEarth : Earth {}
====
Audio compression components, DIB graphics controls, ECO extensions,
FastStrings
www.droopyeyes.com
====
 
 

Re:No virtual class methods in dotnet

Peter Morris [Droopy eyes software] writes:
Quote
I found virtual class methods in Delphi very useful for doing stuff like
this
The equivalent to virtual class methods in other languages which don't
have metaclasses is the Factory pattern. Effectively, metaclasses are
Singleton Factories.
Of course, factories are more traditionally focused solely on
indirecting / abstracting the construction of instances.
Quote
Can anyone think of a better solution? Virtual class methods would be so
nice but as I am writing in C# these are not an option I have.
There are some other possibilities:
1) Use attributes to declaratively describe the information, and have a
helper static method that can answer using the attributes. Derive from
System.Attribute and end the class name in "Attribute", etc.
2) Use an attribute on a static method to label it as the method to
call, if the decision can not be easily encoded in declarative attributes.
-- Barry
--
barrkel.blogspot.com/
 

Re:No virtual class methods in dotnet

"Peter Morris [Droopy eyes software]" <XXXX@XXXXX.COM>a écrit
dans le message de news: XXXX@XXXXX.COM...
|I found virtual class methods in Delphi very useful for doing stuff like
| this
|
| TSignalReceiver = class.......
| public
| function MayAcceptSignal(Signal: TSignal); virtual;
| procedure AcceptSignal(Signal: TSignal); virtual;
| end;
|
| TSignal = class.....
| public
| class function MayExecute(SignalReceiver: TSignalReceiver): boolean;
| virtual;
| procedure Execute(SignalReceiver: TSignalReceiver); virtual;
| end;
|
|
| To get a list of valid signals for my receiver I can iterate a list of
class
| types descended from TSignal and check Signal.MayExecute(Receiver), if it
| returns True then I can display the signal as a valid type to send to my
| receiver instance.
I would go with Barry on this one and use Attributes to solve this kind of
"metadata extension" requirement.
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited =
true)]
public class MayExecuteAttribute : Attribute
{
private bool mayExecute;
public MayExecuteAttribute(bool mayExecute)
{
this.mayExecute = mayExecute;
}
public bool MayExecute
{
get { return mayExecute; }
}
}
[MayExecute(false)]
public abstract class Signal
{
}
[MayExecute(true)]
public class OneSignal : Signal
{
}
[MayExecute(false)]
public class TwoSignal : Signal
{
}
Joanna
--
Joanna Carter [TeamB]
Consultant Software Engineer
 

Re:No virtual class methods in dotnet

Quote
[MayExecute(false)]
public abstract class Signal
{
}

[MayExecute(true)]
public class OneSignal : Signal
{
}
It's not as simple as true/false. The MayExecute could be based on the type
of object being executed on or even a specific instance.
I tried this
1) A static SignalPermission class
2) Each signal class has a static constructor that calls
SignalPermission.AddPermission(typeof(MySignal), new
MayExecuteSignalDelegate(MayExecute));
3) MySignal.Execute is a static method that returns a bool.
Then I can do this
if (SignalPermission.MayExecute(typeof(MySignal), this))
........
Unfortunately the static constructor of MySignal is not called in the above
line. The static constructor is only called when I create my first ever
instance of MySignal, which I found to be most disappointing.
Maybe I could combine the two and have
[Signal(new MayExecuteSignalDelegate(MayExecute))]
I'll have to check if it works.
--
Pete
Blessed are the geek, for they shall public class GeekEarth : Earth {}
====
Audio compression components, DIB graphics controls, ECO extensions,
FastStrings
www.droopyeyes.com
====
 

Re:No virtual class methods in dotnet

Peter Morris [Droopy eyes software] writes:
Quote
Maybe I could combine the two and have

[Signal(new MayExecuteSignalDelegate(MayExecute))]
This won't work - the arguments to attributes can only be constant,
typeof or array-creation expressions.
What you could do is put the attribute on the MayExecute method. Then,
create a static method that looks for the method with the right
attribute using reflection, and possibly cache the result in a
dictionary of Type to delegates to avoid the overhead of reflection for
subsequent invocations.
-- Barry
--
barrkel.blogspot.com/
 

Re:No virtual class methods in dotnet

Barry Kelly writes:
Quote
What you could do is put the attribute on the MayExecute method. Then,
create a static method that looks for the method with the right
attribute using reflection, and possibly cache the result in a
dictionary of Type to delegates to avoid the overhead of reflection for
subsequent invocations.
Here's an example of what I mean:
---8<---
using System;
using System.Reflection;
using System.Collections.Generic;
[AttributeUsage(AttributeTargets.Method,
Inherited=false, // Static methods aren't inherited as such,
// they always live in their declaring class.
AllowMultiple=false)]
public class TheRightMethodAttribute : Attribute
{
}
class App
{
static void Main()
{
InvokeTheRightMethod(typeof(App));
InvokeTheRightMethod(typeof(App2));
InvokeTheRightMethod(typeof(App3));
}
delegate void TheMethodDelegate();
static Dictionary<Type,TheMethodDelegate>_cache =
new Dictionary<Type,TheMethodDelegate>();
static void InvokeTheRightMethod(Type type)
{
TheMethodDelegate del = null;
lock (_cache)
_cache.TryGetValue(type, out del);
// Don't execute arbitrary code while holding
// a lock.
if (del != null)
{
del();
return;
}
MethodInfo found = null;
// Search, towards ancestors, for a static method
// with the attribute.
for (Type t = type;
found == null && t != null;
t = t.BaseType)
{
foreach (MethodInfo method in t.GetMethods(
BindingFlags.Public | BindingFlags.Static))
{
object[] attr = method.GetCustomAttributes(
typeof(TheRightMethodAttribute),
false);
if (attr.Length>0)
found = method;
}
}
if (found == null)
throw new InvalidOperationException(
"No method with TheRightMethod attribute found.");
// Double-check to avoid race to add.
lock (_cache)
if (!_cache.TryGetValue(type, out del))
{
del = (TheMethodDelegate)
Delegate.CreateDelegate(
typeof(TheMethodDelegate), found);
_cache.Add(type, del);
}
del();
}
[TheRightMethod]
public static void MyMethod()
{
Console.WriteLine("foo");
}
}
class App2 : App
{
[TheRightMethod]
public static void MyMethod2()
{
Console.WriteLine("bar");
}
}
class App3 : App
{
// use inherited
}
--->8---
-- Barry
--
barrkel.blogspot.com/
 

Re:No virtual class methods in dotnet

I'll post my final solution very soon. it is working nicely, virtual class
methods would have been much easier though!
 

Re:No virtual class methods in dotnet

On first request each DLL is reflected over to see if it is tagged with a
SignalLibraryAttribute.
If it is then each class is reflected over to see if it supports ISignal.
If it does then a static method is searched for like so:
static bool MayExecute(ISignalTarget signalTarget);
If that method exists then a SignalMayExecuteDelegate is created pointing to
it.
The delegate is then added to a Hashtable keyed on Type.
To check for permission I just do this....
signalMayExecutePermissionEntry =
(SignalMayExecutePermissionEntry)SignalMayExecutePermissions[signalType];
return signalMayExecutePermissionEntry.MayExecute(signalTarget);
It would have been so much quicker and easier with strong type references
and virtual static methods!
Full source code is as follows:
using System;
using System.Collections;
using System.Reflection;
namespace DroopyEyes.Signals
{
public delegate bool SignalMayExecuteDelegate(ISignalTarget signalTarget);
public sealed class SignalManager
{
#region Private
private static object SyncRoot = new Object();
private static Hashtable signalMayExecutePermissions;
private static Hashtable SignalMayExecutePermissions
{
get
{
if (signalMayExecutePermissions == null)
Initialize();
return signalMayExecutePermissions;
}
}
private SignalManager()
{
}
private static void Initialize()
{
lock(SyncRoot)
{
if (signalMayExecutePermissions != null)
return;
signalMayExecutePermissions = new Hashtable();
foreach(Assembly currentAssembly in
AppDomain.CurrentDomain.GetAssemblies())
foreach(Attribute currentAttribute in
currentAssembly.GetCustomAttributes(true))
if (currentAttribute is SignalLibraryAttribute)
RegisterSignalLibrary(currentAssembly);
}
}
private static void RegisterSignalType(Type signalType)
{
MethodInfo methodInfo = signalType.GetMethod("MayExecute",
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
if (methodInfo == null)
return;
if (!methodInfo.IsStatic)
return;
if (methodInfo.ReturnType != typeof(bool))
return;
if (methodInfo.GetParameters().Length != 1)
return;
if (methodInfo.GetParameters()[0].ParameterType != typeof(ISignalTarget))
return;
SignalMayExecuteDelegate signalMayExecuteDelegate;
signalMayExecuteDelegate =
(SignalMayExecuteDelegate)Delegate.CreateDelegate(typeof(SignalMayExecuteDelegate),
methodInfo);
SignalMayExecutePermissionEntry signalMayExecutePermission =
new SignalMayExecutePermissionEntry(signalType, new
SignalMayExecuteDelegate(signalMayExecuteDelegate));
lock(SyncRoot)
{
SignalMayExecutePermissions.Add(signalType, signalMayExecutePermission);
}//lock
}
#endregion
#region Public
public static void RegisterSignalLibrary(Assembly signalLibrary)
{
Type signalType = typeof(Signal);
foreach(Type currentType in signalLibrary.GetTypes())
if (!currentType.IsAbstract && currentType.IsSubclassOf(signalType))
RegisterSignalType(currentType);
}
public static bool MayExecute(ISignalTarget signalTarget, Type signalType)
{
AcceptSignalPermission signalPermission =
signalTarget.MayAcceptSignalType(signalType);
switch (signalPermission)
{
case AcceptSignalPermission.ProhibitedSignalType:
return false;
case AcceptSignalPermission.SupportedSignalType:
return true;
case AcceptSignalPermission.UnknownSignalType:
SignalMayExecutePermissionEntry signalMayExecutePermissionEntry = null;
lock(SyncRoot)
{
if (SignalMayExecutePermissions.ContainsKey(signalType))
signalMayExecutePermissionEntry =
(SignalMayExecutePermissionEntry)SignalMayExecutePermissions[signalType];
}
if (signalMayExecutePermissionEntry != null)
return signalMayExecutePermissionEntry.MayExecute(signalTarget);
return false;
default:
throw new ArgumentException("Unknown AcceptSignalPermission: " +
signalPermission.ToString());
}
}
public static Type[] GetAvailableSignals(ISignalTarget signalTarget)
{
ArrayList allSignalTypes = new ArrayList();
lock(SyncRoot)
{
foreach(DictionaryEntry currentEntry in SignalMayExecutePermissions)
allSignalTypes.Add(currentEntry.Key);
}//lock
ArrayList getAvailableSignalsResult = new ArrayList();
foreach(Type currentSignalType in allSignalTypes)
if (MayExecute(signalTarget, currentSignalType))
getAvailableSignalsResult.Add(currentSignalType);
return (Type[])getAvailableSignalsResult.ToArray(typeof(Type));
}
#endregion
}
[AttributeUsage(AttributeTargets.Assembly)]
public sealed class SignalLibraryAttribute : Attribute
{
public SignalLibraryAttribute() {}
}
internal class SignalMayExecutePermissionEntry
{
internal readonly Type SignalType;
internal readonly SignalMayExecuteDelegate MayExecute;
internal SignalMayExecutePermissionEntry(Type signalType,
SignalMayExecuteDelegate mayExecute)
{
if (signalType == null)
throw new ArgumentNullException("signalType");
if (! signalType.IsSubclassOf(typeof(Signal)))
throw new ArgumentException("signalType is not a subclass of Signal");
if (mayExecute == null)
throw new ArgumentNullException("mayExecute");
SignalType = signalType;
MayExecute = mayExecute;
}
}
}
--
Pete
Blessed are the geek, for they shall public class GeekEarth : Earth {}
====
Audio compression components, DIB graphics controls, ECO extensions,
FastStrings
www.droopyeyes.com
====