-1

I have several objects that inherit from a TreeTagModel. The inheriting objects are meant to be placed in a tree structure and retrieved with limited need to understand what "it" is until the object is consumed. By this I mean, it can get passed through a few functions as TreeTagModel before it is ever understood what sub class type it is.

However, the code becomes significantly more convoluted, once I include a predicate to ensure all inheriting classes implement a ResetIfSameAsRestoreModel(T restoreModel). There is a need for a large number of changes through the code to indicate what kind of TreeTagModel is being used.

For example: I can no longer pass the TreeTagModel parameter. Instead a TreeTagModel, TreeTagModel, etc is required which seems to be going away from the initial (albeit maybe ill-advised) intention of the classification as a TreeTagModel object instead of the subclass. Additionally, moving from a TreeTagModel to the original subclass requires additional classification and I am not longer able to just rely on a call to if(typeof(FordModel).IsInstanceOf(curTreeModel)). Instead I have to know that it is a FordModel (or maybe just use object but I have not tried that) so I can set the object via: TreeTagModel<FordModel> curTreeTag = GetSomeTreeTagModel();

My question is:

  • Is there a better way this should be implemented to ensure that any new TreeTagModel inheriting objects are forced to implement a ResetIfSameAsRestoreModel(subClassObjectType resetModel ) function. As my current approach that moved from abstract TreeTagModel to abstract TreeTagModel<T> made the code significantly more cumbersome and less readable.
//Legacy approach
public abstract TreeTagModel : AnotherBaseModel
{
   //Previously used just as a container to categorize specific 
   //inherited object that are handled in a slightly different way.
   //i.e. the object that inherit from treeTagModel can be placed in 
   //a tree structure
}

public class FordModel : TreeTagModel
{ ... }

public class ChevyModel : TreeTagModel
{ ... }

public class VolvoModel : TreeTagModel
{ ... }

In an attempt to force the inclusion of ResetIfSameAsRestoreModel(...) I have made the following changes

//Current attempt to ensure all subclasses have there version of ResetIfSameAsRestoreModel(...)
public abstract TreeTagModel<T> : AnotherBaseModel
{
    protected abstract bool ResetIfSameAsRestoreModel(T restoreModel);
}

public class FordModel : TreeTagModel<FordModel>
{ 
   ... 
   protected override bool ResetIfSameAsRestoreModel(FordModel restoreModel)
   {
       throw new NotImplementedException();
   }
}

public class ChevyModel : TreeTagModel<ChevyModel>
{ 
    ... 
   protected override bool ResetIfSameAsRestoreModel(ChevyModel restoreModel)
   {
       throw new NotImplementedException();
   }
}

public class VolvoModel : TreeTagModel<VolvoModel>
{ 
    ... 
   protected override bool ResetIfSameAsRestoreModel(VolvoModel restoreModel)
   {
       throw new NotImplementedException();
   }
}
Siberian
  • 27
  • 5

1 Answers1

0

Generic

I'm not 100% sure I understand your question, but if my guess is right, you can use a self-referencing type constraint to accomplish this.

It looks a little weird until you've worked with it a bit:

abstract class TreeTagModel<T> where T : TreeTagModel<T>
{
    protected abstract bool ResetIfSameAsRestoreModel(T restoreModel);
}

Now you can write this:

class FordModel : TreeTagModel<FordModel>
{
    protected override bool ResetIfSameAsRestoreModel(FordModel restoreModel)
    {
        //Do something with FordModel
        return true;
    }
}

Non-Generic

If you'd rather avoid the generics, you can use standard polymorphism and a run-time type check. Here we split the code into two methods, one that does the type check and one that the descendent class can override.

abstract class TreeTagModel
{
    protected bool ResetIfSameAsRestoreModel(TreeTagModel restoreModel)
    {
        if (restoreModel.GetType() == this.GetType())
        {
            Reset();
            return true;
        }
        else
        {
            return false;
        }
    }

    protected abstract void Reset();
}

class FordModel : TreeTagModel
{
    protected override void Reset()
    {
        //Perform your type-specific logic here
    }
}
John Wu
  • 50,556
  • 8
  • 44
  • 80
  • You keep talking about the superclass in spots where I think you ought to be talking about the subclass. Are you sure you don't have the two terms mixed up? – John Wu Sep 24 '21 at 00:41
  • You are right. Yes I am mixing these two. Thank you for bringing that to my attention. – Siberian Sep 24 '21 at 02:59
  • REDO: Hey John, thanks. I should have been a bit more descriptive with my function name. ResetIfSameAsRestoreModel(...) would be better described as ResetIfEqual. So the idea it less about allowing passing of a different type through ResetIfSameAsRestoreModel(...) and more about if the members of the object (that are guaranteed to be of the same type) are all the same then reset(). i.e. TreeTagModel would not be allowed as a parameter. Instead the subclass object is a parameter. I will reattempt with where T : TreeTagModel That may be slightly different than what I used before not sure... – Siberian Sep 24 '21 at 03:03