So why isn't it allowed to have Shared
MustOverride
/Overridable
members? Some argue that overriding is related to inheritance, which doesn't make sense in case of Shared
members because there is no instantiation involved. Here is one example where I need it:
My base class DrawingObject
defines a Shared
member named TypeName
that must be implemented by each child class to return a unique identification string, which is different for each child type, but same for all instance of one child type. Now this requires me to have TypeName
property defined as Shared
and Overridable
. Or is there a better way of doing it?
Base class
Public MustInherit Class DrawingObject
Public MustOverride ReadOnly Property TypeName As String
End Class
Child class
Public Class Rectangle
Inherits DrawingObject
Public Overrides ReadOnly Property TypeName As String
Get
Return A_CONST_STRING_DEFINED_IN_THIS_CLASS
End Get
End Property
End Class
This code works fine, but ideally TypeName
should have been Shared
since it returns a Const
.
Specifies that a property or procedure is not implemented in this class and must be overridden in a derived class before it can be used.
An MustOverride method has no implementation. It creates a method name and signature that must be implemented in all derived classes. Furthermore, making one or more methods of any class MustOverride has the side effect of making the class abstract; an abstract class must be marked with the keyword MustInherit .
To prevent being overridden, use the sealed in C#. When you use sealed modifiers in C# on a method, then the method loses its capabilities of overriding. The sealed method should be part of a derived class and the method must be an overridden method.
The whole point of overriding is to facilitate polymorphism. You pass an object around and the way it behaves depends on the type of the object rather than the type of the reference. If you're calling Shared
members then you're calling them on a type rather than an object so polymorphism doesn't apply so overriding offers no advantage.
In your case, if you want to get the TypeName
of an object without knowing what type that object is at run time then overriding that property would make sense. No matter where you are, you can get that property and you'll get the name of the type of that object. With a Shared member, you're going to be getting the property on a specific type so you can simply get a property of that type.
Requested Example:
Let's say that you have a shapes that know how to draw themselves on screen. You might start with a base Shape
class with a base Shape
class with a Draw
method and then inherit that class in, for example, Square
and Circle
classes. You could then have a method like this:
Public Sub DrawShape(myShape As Shape)
myShape.Draw()
End Sub
In that case it makes sense to override the Draw
method in the derived classes because doing so allows you to simply call Draw
wherever you have a Shape
reference and know that it will be drawn correctly. If that method is passed a Square
then a square will be drawn and if it is passed a Circle
then a circle would be drawn, but the method doesn't have to know or care, thanks to polymorphism.
If what you are suggesting was possible though, and Draw
was a Shared
method, you would have to call Square.Draw
every time you wanted to draw a square and Circle.Draw
every time you wanted to draw a circle. The whole point of overriding is that you're supposed to be able to call the method on a reference of the base type and get functionality defined in the derived type. In your scenario, you'd have to call the method on the derived type to get functionality defined in the derived type, so you get no advantage. You couldn't just call Shape.Draw
and have anything useful happen. Apart from anything else, which derived class would it choose?
You are trying to create a self-describing class.
There are 3 methods:
Custom Attributes:
This is a simple example I made:
Imports System.Linq
Imports System.Runtime.CompilerServices
<AttributeUsage(System.AttributeTargets.[Class])>
Public Class SelfDescribingClassAttribute
Inherits System.Attribute
Public Property Name As String
Public Sub New(Name As String)
Me.Name = Name
End Sub
End Class
<SelfDescribingClassAttribute("ExampleClassName")>
Public Class ExampleClass
End Class
Public Module SelfDescribingClassTools
Public Function GetNameOfSelfDescribingClass(ClassType As Type) As String
Try
GetNameOfSelfDescribingClass = ClassType.GetAttributeValue(Function(SelfDescribingClass As SelfDescribingClassAttribute) SelfDescribingClass.Name)
Catch ex As Exception
Return String.Empty
End Try
End Function
Public Function GetDictionaryOfSelfDescribingClasses(Of T)() As Dictionary(Of String, Type)
GetDictionaryOfSelfDescribingClasses = New Dictionary(Of String, Type)
Dim Subclasses As Type() = GetSubClasses(Of T)()
For Each Subclass As Type In Subclasses
Try
Dim name As String = GetNameOfSelfDescribingClass(Subclass)
If Not String.IsNullOrWhiteSpace(name) Then
GetDictionaryOfSelfDescribingClasses.Add(name, Subclass)
End If
Catch ex As Exception
Debug.Print(ex.ToString)
End Try
Next
End Function
Public Function GetSubClasses(Of T)() As Type()
Dim baseType As Type = GetType(T)
Dim assembly As Reflection.Assembly = baseType.Assembly
Return assembly.GetTypes().Where(Function(x) x.IsSubclassOf(baseType))
End Function
<Extension()>
Function GetAttributeValue(Of TAttribute As Attribute, TValue)(ByVal type As Type, ByVal valueSelector As Func(Of TAttribute, TValue)) As TValue
Dim att = TryCast(type.GetCustomAttributes(GetType(TAttribute), True).FirstOrDefault(), TAttribute)
If att IsNot Nothing Then
Return valueSelector(att)
End If
Return Nothing
End Function
End Module
Self-registering classes:
This is a really good writeup with examples: http://www.jkfill.com/2010/12/29/self-registering-factories-in-c-sharp/
From the site:
DataType.cs:
using System;
using System.Collections.Generic;
using System.Reflection;
namespace SelfRegisteringFactory
{
public abstract class DataType
{
public static DataType Create(string typeName)
{
Type derivedType = null;
if (sTypeMap.TryGetValue(typeName, out derivedType))
{
return System.Activator.CreateInstance(derivedType)
as DataType;
}
return null;
}
public abstract string GetDefaultValue();
protected abstract string GetTypeName();
private static Dictionary<string, Type> sTypeMap = CreateTypeMap();
private static Dictionary<string, Type> CreateTypeMap()
{
Dictionary<string, Type> typeMap =
new Dictionary<string, Type>();
Assembly currAssembly = Assembly.GetExecutingAssembly();
Type baseType = typeof(DataType);
foreach (Type type in currAssembly.GetTypes())
{
if (!type.IsClass || type.IsAbstract ||
!type.IsSubclassOf(baseType))
{
continue;
}
DataType derivedObject =
System.Activator.CreateInstance(type) as DataType;
if (derivedObject != null)
{
typeMap.Add(
derivedObject.GetTypeName(),
derivedObject.GetType());
}
}
return typeMap;
}
}
}
BooleanDataType.cs:
using System;
namespace SelfRegisteringFactory
{
public class BooleanDataType : DataType
{
public BooleanDataType()
{
}
public override string GetDefaultValue()
{
return "false";
}
protected override string GetTypeName()
{
return "bool";
}
}
}
IntegerDataType.cs:
using System;
namespace SelfRegisteringFactory
{
public class IntegerDataType : DataType
{
public IntegerDataType ()
{
}
public override string GetDefaultValue ()
{
return "0";
}
protected override string GetTypeName ()
{
return "int";
}
}
}
Main.cs:
using System;
namespace SelfRegisteringFactory
{
class MainClass
{
public static void Main (string[] args)
{
PrintDefaultForType("bool");
PrintDefaultForType("int");
}
public static void PrintDefaultForType(string typeName)
{
DataType dataType = DataType.Create(typeName);
if (dataType != null)
{
Console.WriteLine(dataType.GetDefaultValue());
}
else
{
Console.WriteLine("unknown");
}
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With