没有使用泛型的显式叶节点类的父子数据结构

时间:2011-06-13 13:42:34

标签: c# c#-4.0 covariance generics

我有以下示例关系:

namespace Yesod
{
    public class Program
    {
        //
        //
        //
        public struct Particle
        {
            public byte type;
        }

        //
        //
        //
        public class Entity<T>
        {
            public Entity<Entity<T>> Parent
            { get; private set; }

            //
            //
            //
            public Entity(Entity<Entity<T>> parent)
            {
                this.Parent = parent;
            }
        }

        //
        //
        //
        public sealed class Atom : Entity<Particle>
        {
            public Atom(Entity<Atom> parent)
                : base(parent) // Compile Error.
            { }
        }

        //
        //
        //
        public sealed class Molecule : Entity<Atom>
        {
            public Molecule()
                : base(null)
            { }
        }

        static void Main(string[] args)
        {

        }
    }
}

如何解决上面产生的以下编译错误?

  Argument 1: cannot convert from 'Yesod.Program.Entity<Yesod.Program.Atom>' to 'Yesod.Program.Entity<Yesod.Program.Entity<Yesod.Program.Particle>>'

评论回复#1: 具体来说,代码正在尝试分配类型为

的对象
Entity<Atom>

类型的对象
Entity<Entity<Particle>>

Atom实现为

public sealed class Atom : Entity<Particle>

,由此

Entity<Atom>

预计将分解为

Entity<Entity<Particle>>

2 个答案:

答案 0 :(得分:0)

我不知道C#,但Java程序员偶尔也会遇到这个问题。

环顾其他C#来源,我认为你可以做到你想要的东西(在类型安全方面有一点损失):

    public class Entity<T>
    {
        public Entity<P> Parent
            where P : Entity<Entity<T>>
        { get; private set; }

        //
        //
        //
        public Entity(Entity<P> parent)
            where P : Entity<Entity<T>>
        {
            this.Parent = parent;
        }
    }

Java答案将涉及? extends Entity<T>。基本问题是虽然MoleculeEntity<Atom>,但编译器无法知道Molecule也是Entity<Entity<Particle>>。毕竟,假设Entity维护了一个孩子列表,并且有一个明智的addChild(T child)方法。然后编译器会希望确保您只添加Atom s作为分子的子项。但如果MoleculeEntity<Entity<Particle>>,那么没有什么可以阻止你这样做:

Entity<Entity<Particle>> downcast = myMolecule;
downcast.addChild(myNonAtomParticleBasedEntity);

适用于此模式的完全类型安全的解决方案涉及自我类型,Java和C#没有。 Foo<F extends Foo<F>>and its C# equivalent)的Java模式非常接近,但使用起来非常滑。除此之外,声明时间差异会使这种模式更清晰。

答案 1 :(得分:0)

尽管丹尼尔·马丁发布的潜在解决方案永远不会起作用(正如已经警告过的那样),但为什么我的代码永远无法工作的解释是100%准确的,这让我发现C#4.0解决了这个原本预期的功能使用新的generic covariance & contra-variance语言功能。

以下是解决方案,供审核:

namespace Yesod
{
    public class Program
    {
        //
        //
        //
        public struct Particle
        {
            public byte type;
        }

        // Fix with C# 4.0 using out keyword.
        //
        //
        public interface IEntity<out T>
        {
            IEntity<IEntity<T>> Parent
            { get; }
        }

        //
        //
        //
        public class Entity<T> : IEntity<T>
        {
            public IEntity<IEntity<T>> Parent
            { get; private set; }

            //
            //
            //
            public Entity(IEntity<IEntity<T>> parent)
            {
                this.Parent = parent;
            }
        }

        //
        //
        //
        public sealed class Atom : Entity<Particle>
        {
            public Atom(Entity<Atom> parent)
                : base(parent) // No more compile error.
            { }
        }

        //
        //
        //
        public sealed class Molecule : Entity<Atom>
        {
            public Molecule()
                : base(null)
            { }
        }

        //
        //
        //
        static void Main(string[] args)
        {
            // Now this can be done.
            Molecule water = new Molecule();
            Atom H1 = new Atom(water);
            Atom O1 = new Atom(water);
            Atom O2 = new Atom(water);
        }
    }
}

谢谢Daniel Martin。由于我目前的代表,我无法帮助你。得分了。对于那些想知道的人,上面是一个愚蠢的模拟,旨在强调明显的亲子关系,以帮助那些人理解这个问题。现实世界的预期用途将是一个高级版本,将用于计算机图形领域,以明确定义和递归的方式解决相干空间分区问题。