【问题标题】:Declare a generic type as property of an interface?将泛型类型声明为接口的属性?
【发布时间】:2015-01-28 16:27:23
【问题描述】:

我有一个 Generic 类型,它用于在对象上提供一些要持久化的元数据:

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;}
}

我有我的IPersistedObject,可以给出他的定义

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

这个想法是,如果我实现IPersistedObject,我应该这样实现它:

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

当我坚持我的班级有以下事情:

不能执行以下操作:

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

因为:

  1. 它允许有一个MyPersistedObject&lt;SomeOtherObject
  2. 有时我收到一个对象,我应该能够查看它是否实现了 IPersistedObject 并使用它执行一些自定义操作。

对于 2,如果我有一个通用接口,下面是一个例子,说明我面临什么样的问题:

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);
        }
    }
    ...
}

在 C# 中是否有可能声明具有实现类型的泛型属性的接口?

【问题讨论】:

  • 还有什么问题?
  • 不清楚为什么不能有IPersistedObject&lt;T&gt; 能解释一下吗?最终目标是什么?你想达到什么目的?这看起来像一个 XY 问题。
  • 你想在这里解决什么问题?我不完全理解您的类型层次结构,所以我没有太多帮助,但正如一些建议:仅在可以帮助您轻松推理代码及其编译时间保证的范围内使用类型系统。如果你的类型层次结构让你为了实现一个接口而做心理体操(我以前曾在这个地方做过),你可能最好少一点编译时安全性和多一点头。
  • 你将如何使用所有这些接口?
  • 你基本上是在慢慢走向curiously recurring template pattern,尽管这有其自身的局限性(特别是它没有解决你在上一段中提到的问题:可以定义完全无效接口的实现)。

标签: c# generics


【解决方案1】:

您可以使用奇怪的重复模板模式来进一步锁定它。它不是万无一失的,但是假设您不是受虐狂,并且您不介意理论上可以创建违反您要保证的不变量的接口的荒谬实现,您可以这样做:

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; }
}

正如您一开始就注意到的那样,问题在于您可以简单地定义public class MyPersistedObject : IPersistedObject&lt;MyOtherPersistedObject&gt;,并最终违反您试图拼凑的合同,简而言之如下:

一个持久化对象必须有一个可获取的定义,它是一个它自己类型的持久化元素定义

C# 类型系统根本无法优雅地处理这个问题。我的建议是尽早离开,尽可能改用objectdynamic,并学会忍受失去某些编译时间保证。

假设您愿意牺牲一些编译时安全性,您可以这样做:

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; } }
}

【讨论】:

  • 查看我问题的最后一部分,如果我有IPersistedObject&lt;T&gt;,我会在指定的时刻遇到问题。
  • 这个dynamicthing 看起来很棒,让我试试我的原型,看看我是不是被什么东西挡住了。
  • 您是否认为也可以将PersistedPropertyDefinition 中的object 替换为dynamic?我确实放了它,因为我没有看到其他方法。
  • 伙计,你真是个天才 :) 我今天学到了一个新的 C# 东西,它非常适合我的上下文。非常感谢您分享您的知识。
  • 您好,由于dynamic,我遇到了一些麻烦,看来RuntimeBinder 有内存泄漏。它保留了很多对象的实例(参见:stackoverflow.com/questions/33080252/…)。你有没有遇到过这样的问题?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-07
  • 2017-05-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多