Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Declaring Shared MustOverride

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.

like image 550
dotNET Avatar asked Sep 08 '15 04:09

dotNET


People also ask

What is the purpose of using MustOverride keyword in a class?

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.

What is MustOverride in VB net?

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 .

How do I stop overriding in C#?

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.


2 Answers

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?

like image 183
jmcilhinney Avatar answered Oct 18 '22 07:10

jmcilhinney


You are trying to create a self-describing class.

There are 3 methods:

  1. Dependency injection: use a framework such as unity (heavy but may really be what you want)
  2. Custom Attributes: to add metadata to the class and then scan the metadata
  3. 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");
            }
        }
    }
}
like image 1
VoteCoffee Avatar answered Oct 18 '22 06:10

VoteCoffee