You are trying to create a self-describing class.
There are 3 methods:
- Dependency injection: use a framework such as unity (heavy but may really be what you want)
- Custom Attributes: to add metadata to the class and then scan the metadata
- Self-registering classes (instantiate and destroy each subclass to access mustoverride properties). Makes use of the factory pattern.
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");
}
}
}
}