1

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?

J4N
  • 19,480
  • 39
  • 187
  • 340
  • and What is the question ? – Rohit Prakash Jan 28 '15 at 07:17
  • 1
    It's not clear why you can't have a `IPersistedObject` can you explain a little bit? and what's the ultimate goal? what are you trying to achieve? This looks like a XY problem. – Sriram Sakthivel Jan 28 '15 at 07:18
  • What problem are you trying to solve here? I don't entirely understand your type hierarchy so I'm not much help, but just as some advice: use the type system only in so far as it helps you reason easily about your code and its compile time guarantees. If your type hierarchy has you doing mental gymnastics just to implement an interface (I've been in this spot before) you're probably better off with a little less compile time safety and a little more hair on your head. – Asad Saeeduddin Jan 28 '15 at 07:20
  • How would you use all those interfaces? – Euphoric Jan 28 '15 at 07:22
  • 1
    What you're basically inching towards is the [curiously recurring template pattern](http://blogs.msdn.com/b/ericlippert/archive/2011/02/03/curiouser-and-curiouser.aspx), although this has its own limitations (specifically it doesn't address the problem you mentioned in your last paragraph: it is possible to define completely invalid implementations of the interface). – Asad Saeeduddin Jan 28 '15 at 07:32
  • @RohitPrakash Right ;) I added it, was clear in my head, not in my question, sorry – J4N Jan 28 '15 at 07:55
  • This is invalid syntax, can you show us the real code? `public interface IPersistedObject{ PersistedElementDefinition Definition {get;} }` – Lasse V. Karlsen Jan 28 '15 at 07:55
  • @SriramSakthivel I added some information in my question – J4N Jan 28 '15 at 07:56
  • @Asad The issue behind this is that the `PersistedElementDefinition` own a list of an other object, with every element of this list defining a property to persist, with a `Func` and an `Action` to be provided to get/set the property on the current object. – J4N Jan 28 '15 at 07:59
  • @Euphoric I added some example – J4N Jan 28 '15 at 08:11
  • @J4N Is it possible for `PersistedElementDefinition` (and the elements in its list) to use dynamic dispatch instead of having a `T : IPersistedObject` generic parameter? – Asad Saeeduddin Jan 28 '15 at 08:11
  • What is dynamic dispatch? Using reflection? No I'm sorry I can't use this for performance reason. – J4N Jan 28 '15 at 08:20
  • @Asad I didn't heard of dynamic dispatch before, but it opens a new whole lot of possibilities. I will make some research on this, not sure how to use it in my case – J4N Jan 28 '15 at 08:33
  • @J4N I have added an example of how you could use it for your case. You lose type safety at the location where you access the `Definition` property, but thereafter everything works as normal. This isn't the only way you could architect your code around dynamic dispatch (and I'm sure if you thought about it you could come up with a better way), but this is the most naive approach. – Asad Saeeduddin Jan 28 '15 at 08:36

1 Answers1

3

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; } }
}
Asad Saeeduddin
  • 46,193
  • 6
  • 90
  • 139
  • See the last part of my question, if I've a `IPersistedObject` I will have an issue at a the specified moment. – J4N Jan 28 '15 at 08:13
  • This `dynamic`thing seems awesome, let me try in my prototype to see if I'm blocked by something. – J4N Jan 28 '15 at 08:38
  • Do you think it's possible to also replace my `object`in the `PersistedPropertyDefinition` by `dynamic`? I did put it because I didn't see how to do it otherwise. – J4N Jan 28 '15 at 08:46
  • Dude, you are a genious :) I learned a new C# thing today, which fits just perfectly in my context. Thank you so much for sharing your knowledge. – J4N Jan 28 '15 at 11:30
  • Hi again, I ran in some trouble due to the `dynamic`, it seems that the RuntimeBinder has a memory leak. It keeps an instance of a lot of object( See : http://stackoverflow.com/questions/33080252/memory-overflow-having-an-increasing-number-of-microsoft-csharp-runtimebinder-s ). Any chance you alreay ran in such issue? – J4N Nov 30 '15 at 07:27
  • @J4N You've probably already fixed it, but sadly no, I have no useful advice re: dynamic performance problems. In certain contexts `object` does just as well as `dynamic`, but you need to cast the `object` reference before use. Probably that won't have the memory leak issue. – Asad Saeeduddin Nov 27 '16 at 04:27
  • @J4N There is a workaround suggested here: https://connectbeta.microsoft.com/VisualStudio/feedback/details/1925659/runtimebinder-leaks-when-dynamic-keyword-used-with-comobject – Asad Saeeduddin Nov 27 '16 at 04:29