Byte Commander
Byte Commander

Reputation: 6736

inheritance of abstract class with static property in C#

Short version:

I have an abstract class A. It has a method that needs to know the value of a static class property that is specific to each subclass. The name and type is the same, just the value can be unique for each subclass.

Can I define this static property in the base class A to be able to access it with methods defined in A, but keeping the property's values of different subclasses unrelated?

Or how would I implement something like that?


Long version:

Let's say I have an abstract base class for data models. It has a public property Id (Int32).

I would like to implement a constructor in the base class that generates a new ID based on the last assigned ID for objects of a subclass.

The reason is that the real IDs are assigned automatically by the database, but each data model object already has to have a unique ID when it gets constructed without being written to the database yet. As the database assigns only positive integers as ID, my plan is to assign newly created data model objects a temporary, unique negative ID. As soon as the object gets written, the ID will get changed to the real one.

As I have quite a few different data model classes all deriving from my abstract base class, I thought it would be good to include that functionality there to not duplicate it. But each subclass has to have their own counter that points to the next free negative ID, as the different classes' IDs are unrelated.

So I need a static property in each subclass storing this class' last assigned temporary ID, but the mechanism to assign it is always the same and could be implemented into the abstract base class' constructor. However, I can't access a property from the base class that has to be implemented by the subclasses, which means I have to define it in the base class. But will this static property then be global for all subclasses, which is not what I want?

How can I implement this temporary ID counter the most elegant way?

Simplified code example:

public abstract class ModelBase
{
    public Int32 Id { get; set; }
    protected static Int32 LastTempId { get; set; } = 0;

    public ModelBase()
    {
        Id = --LastTempId;
    }
}


public class Model1 : ModelBase
{
    public Model1 () : base ()
    {
        // do something model1-specific
    }
}

public class Model2 : ModelBase
{
    public Model2() : base()
    {
        // do something model2-specific
    }
}

If I implement it like this, I fear that for both subclasses model1 and model2, the inherited static property LastTempId will be the same instance. But I want a separate counter for each subclass while still using it in the base class constructor.

Upvotes: 8

Views: 11739

Answers (5)

D Stanley
D Stanley

Reputation: 152556

Well, static classes aren't inherited, so that's out, and you can't force subclasses to implement a static method, so that's out too.

Rather than putting that method in the class itself, why not have a base interface that you can implement. Then you can have an instance method that can be abstract:

public interface IDataModelFactory<T> where T:ModelBase
{
    int GetLastTempId();
}

public Model1Factory : IDataModelFactory<Model1>
{
    public int GetLastTempId()
    {
        // logic for Model1 
    }
}

public Model2Factory : IDataModelFactory<Model2>
{
    public int GetLastTempId()
    {
        // logic for Model2
    }
}

Or if the logic is common to all classes, have an abstract base class with (or without) the interface:

public DataModelFactory<T> : IDataModelFactory<T>
{
    public virtual int GetLastTempId()
    {
        // common logic
    }

    // other common logic
}

You could even make the factories singletons so you don't have to create instances all the time, and they can even be sub-classes of the model classes so they're closely linked.

As a side note, if you're uncertain what the inheritance/interface relationship would be, I often find it's quicker start with copy/paste reuse and refactor your code to introduce base classes and interfaces. That way you know what the common code is and can refactor that into common methods. Otherwise you are tempted to try and put everything in the base class and use switches or other constructs to change logic based on the derived type.

Upvotes: 0

bodangly
bodangly

Reputation: 2624

Just generate a GUID for each object before it gets added to your database. You could have an isAdded flag that tells you the object should be referred to be GUID, or clear the GUID once the object is added. With a GUID you never have to worry that two objects will clash. Also it obviates the need for separate IDs per subclass. I would not reuse the same property for two states as you propose.

https://msdn.microsoft.com/en-us/library/system.guid(v=vs.110).aspx

Upvotes: 0

Mat&#237;as Fidemraizer
Mat&#237;as Fidemraizer

Reputation: 64923

First of all, my humble opinion is entities shouldn't be responsible of assigning their own unique identifier. Keep a clear separation of concerns.

There should be another player in that game that should assign those temporary unique identifiers (either if they're negative or positive integers).

Usually, that so-called other player is an implementation of repository design pattern which is responsible of translating the domain (your models) into the definitive representation of your data and vice versa.

Usually a repository has a method to add objects. And this should be the point where you set these temporary identifiers:

public void Add(Some some)
{
    some.Id = [call method here to set the whole id];
}

And, most repository implementations are per entity.

  • CustomerRepository
  • InvoiceRepository
  • ...

...but this doesn't prevent you from defining a base repository class which could implement what can be in common when handling some entity types:

   public interface IRepository<TEntity> where TEntity : EntityBase
   {
         // Other repository methods should be defined here
         // but I just define Add for the convenience of this 
         // Q&A
         void Add(TEntity entity);
   }


   public class Repository<TEntity> : IRepository<TEntity>
          where TEntity : EntityBase
   {
         public virtual void Add(TEntity entity)
         {
              entity.Id = [call method here to set the whole id];
         }
   }

...and now any class deriving Repository<TEntity> will be able to generate a temporary identifier for their specialized entities:

   public class CustomerRepository : Repository<Customer> { }
   public class InvoiceRepository : Repository<Invoice> { }

How you could implement the unique and temporary entity identifier as part of the abstract repository class and being able to do so for each specific entity type?

Use a dictionary to store per-entity last assigned identifier implementing a property to Repository<TEntity>:

public Dictionary<Type, int> EntityIdentifiers { get; } = new Dictionary<Type, int>();

...and a method to decrease next temporary identifier:

private static readonly object _syncLock = new object();

protected virtual void GetNextId()
{
     int nextId;

     // With thread-safety to avoid unwanted scenarios.
     lock(_syncLock)
     {
          // Try to get last entity type id. Maybe the id doesn't exist
          // and out parameter will set default Int32 value (i.e. 0).
          bool init = EntityIdentifiers.TryGetValue(typeof(TEntity), out nextId);
          // Now decrease once nextId and set it to EntityIdentifiers
          nextId--;

          if(!init)
               EntityIdentifiers[typeof(TEntity)] = nextId;
          else
               EntityIdentifiers.Add(typeof(TEntity), nextId);
     }

     return nextId;    
}

Finally, your Add method could look as follows:

public virtual void Add(TEntity entity)
{
     entity.Id = GetNextId();
}

Upvotes: 4

Marco Scabbiolo
Marco Scabbiolo

Reputation: 7449

Short answer

The sub-classes cannot have different values for the static property because the static property is a property of the class, not of it's instances, and it's not inherited.

Long answer

You could implement a single counter on the abstract class as a static property and have one constructor of the abstract class using it.

EDIT: To save different counters for each sub-class you could use a static dictionary mapping a Type (sub-class) to a counter.

public abstract class A<T>
{
    public static Dictionary<Type, int> TempIDs = new Dictionary<Type, int>();

    public int ID { get; set; }

    public A()
    {
        if (!TempIDs.ContainsKey(typeof(T)))
            TempIDs.Add(typeof(T), 0);

        this.ID = TempIDs[typeof(T)] - 1;

        TempIDs[typeof(T)]--;
    }
}

public class B : A<B>
{

    public string Foo { get; set; }

    public B(string foo)
        : base()
    {
        this.Foo = foo;
    }
}

public class C : A<C>
{
    public string Bar { get; set; }

    public C(string bar)
        : base()
    {
        this.Bar = bar;
    }
}

B b1 = new B("foo");
B b2 = new B("bar");

C c1 = new C("foo");
C c2 = new C("foo");

b1.ID would be -1, b2.ID would be -2, c1.ID would be -1 and c2.ID would be -2

Upvotes: 7

Kuba Wyrostek
Kuba Wyrostek

Reputation: 6221

One way to go is reflection, but it takes run-time and is prone to runtime errors. As others mentioned: you cannot force inheriting classes to redeclare some static field and be able to use this field in ancestor class. So I think minimal code redundancy is necessary: each inheriting class should provide it's own key generator. This generator can be kept in static field of the class of course.

(Note this is not necessarily thread-safe.)

class KeyGenerator
{
    private int _value = 0;

    public int NextId()
    {
        return --this._value;
    }
}

abstract class ModelBase
{
    private KeyGenerator _generator;

    public ModelBase(KeyGenerator _generator)
    {
        this._generator = _generator;
    }

    public void SaveObject()
    {
        int id = this._generator.NextId();
        Console.WriteLine("Saving " + id.ToString());
    }
}

class Car : ModelBase
{
    private static KeyGenerator carKeyGenerator = new KeyGenerator();

    public Car()
        : base(carKeyGenerator)
    {
    }
}

class Food : ModelBase
{
    private static KeyGenerator foodKeyGenerator = new KeyGenerator();

    public Food()
        : base(foodKeyGenerator)
    {
    }
}

class Program
{
    static void Main(string[] args)
    {
        Food food1 = new Food();
        Food food2 = new Food();
        Car car1 = new Car();
        food1.SaveObject();
        food2.SaveObject();
        car1.SaveObject();
    }
}

This produces:

Saving -1
Saving -2
Saving -1

Upvotes: 1

Related Questions