J4N
J4N

Reputation: 20707

Declare a generic type as property of an interface?

I've a Generic type, which is used to give some meta data on an object to persist:

public class PersistedElementDefinition<T> where T: IPersistedObject{
    List<PersistedPropertyDefinition<T>> PropertiesToPersist {get;set;}
}

public class PersistedPropertyDefinition<T> where T: IPersistedObject{
        public Func<T, object> PropertyGetter{get;set;}
        public Action<T, object> PropertySetter {get;set;}
}

and I've my IPersistedObject which can give his definition

public interface IPersistedObject{
    PersistedElementDefinition<TypeOfTheImplementingType> Definition {get;}
}

The idea is that if I implement IPersistedObject I should implement it like this:

public class MyPersistedObject:IPersistedObject{
    PersistedElementDefinition<MyPersistedObject> Definition{get;}
}

When I persist my class have the following thing:

I can't do the following:

public interface IPersistedObject<T>{
    PersistedElementDefinition<T> Definition {get;}
}

because:

  1. It would allow to have a MyPersistedObject<SomeOtherObject
  2. At some point I receive an object, and I should be able to see if it implements the IPersistedObject and do some custom action with it.

For the 2, here is an example of what kind of issue I'm facing if I've a Generic interface:

public void Persist<T>(T objectToPersist)where T:IPersistedObject{
    ...
    foreach(PersistedPropertyDefinition<T> property in objectToPersist.PropertiesToPersist){
        object objectToSerialize = property.ObjectGetter(objectToPersist);
        if(objectToSerialize is IPersistedObject<___Don't know how to put something generic here___>){
            Persist((IPersistedObject<___Don't know how to put something generic here___>)objectToSerialize);
        }
    }
    ...
}

Is there a possibility in c# to declare an interface with a generic property of the implementing type?

Upvotes: 1

Views: 3879

Answers (1)

user1726343
user1726343

Reputation:

You can use the curiously recurring template pattern to lock this down a bit further. It isn't bulletproof, but assuming you're not a masochist, and you don't mind the fact that it is theoretically possible to create nonsensical implementations of the interface that violate the invariants you are trying to guarantee, you can do this:

public interface IPersistedObject<T> where T : IPersistedObject<T>
{
    PersistedElementDefinition<T> Definition {get;}
}

public class PersistedElementDefinition<T> where T: IPersistedObject<T>
{
    ...
}

public class MyPersistedObject : IPersistedObject<MyPersistedObject>
{
    // Here, you are forced to implement a PersistedElementDefinition<MyPersistedObject>,
    // which presumably is the reason behind this whole song and dance

    PersistedDefinition<MyPersistedObject> Definition { get; }
}

The problem with this, as you noticed at the outset, is that you could simply define public class MyPersistedObject : IPersistedObject<MyOtherPersistedObject>, and end up breaking the contract you are trying to cobble together, which in plain words is the following:

A persisted object must have a gettable definition that is a persisted element definition of its own type

The C# type system is simply not equipped to handle this elegantly. My advice is to get out early, change to object or dynamic where possible and learn to live with the loss of certain compile time guarantees.

Assuming you're willing to sacrifice some compile time safety, you could do things like so:

class Program
{
    static void Main(string[] args)
    {
        var mpo = new MyPersistedObject();
        var ptp = mpo.Definition.PropertiesToPersist;
    }
}

public class PersistedElementDefinition<T> where T : IPersistedObject
{
    private readonly List<PersistedPropertyDefinition<T>> _propsToPersist = new List<PersistedPropertyDefinition<T>>();
    public List<PersistedPropertyDefinition<T>> PropertiesToPersist
    {
        get { return _propsToPersist; }
    }
}

public class PersistedPropertyDefinition<T> where T : IPersistedObject
{
    public Func<T, object> PropertyGetter { get; set; }
    public Action<T, object> PropertySetter { get; set; }
}

public interface IPersistedObject
{
    dynamic Definition { get; }
}

public class MyPersistedObject : IPersistedObject
{
    private readonly PersistedElementDefinition<MyPersistedObject> _definition = new PersistedElementDefinition<MyPersistedObject>();
    public dynamic Definition { get { return _definition; } }
}

Upvotes: 3

Related Questions