Структура данных «родитель-потомок» без явного класса конечных узлов с использованием обобщенных - PullRequest
2 голосов
/ 13 июня 2011

У меня есть следующий пример отношения:

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>

* 1018 в результате чего *

Entity<Atom>

ожидается с разбивкой на

Entity<Entity<Particle>>

Ответы [ 2 ]

0 голосов
/ 14 июня 2011

Несмотря на то, что потенциальное решение, опубликованное Дэниелом Мартином, никогда не сработает (как об этом следует предупреждать), объяснение того, почему мой код тоже никогда не будет работать, является точным на 100%, и это привело меня к обнаружению, что C # 4.0 решает эту иначе ожидаемую функциональностьиспользуя его новые универсальные ковариационные и противоречивые языковые возможности.

Ниже приводится решение для обзора:

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

Спасибо Дэниел Мартин.Я не мог 1 из вас из-за моего текущего представителя.Гол.Для тех, кто интересуется, вышесказанное - глупый макет, нацеленный на то, чтобы подчеркнуть очевидные отношения между родителями и детьми, чтобы помочь людям понять вопрос.Предполагаемое реальное использование будет усовершенствованной версией, которая будет использоваться в области компьютерной графики для четкого и рекурсивного решения проблемы когерентного разбиения пространства.

0 голосов
/ 13 июня 2011

Я не знаю 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>. Основная проблема заключается в том, что, хотя Molecule является Entity<Atom>, компилятор не может знать, что Molecule также является Entity<Entity<Particle>>. В конце концов, предположим, что Entity вел список детей и имел разумный метод addChild(T child). Тогда компилятор захочет убедиться, что вы только добавили Atom s как дочерние элементы молекул. Но если Molecule является Entity<Entity<Particle>>, то ничто не помешает вам сделать:

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

Правильное полностью безопасное для типов решение для этого шаблона включает self types , которого нет в Java и C #. Шаблон Java Foo<F extends Foo<F>> ( и его эквивалент C # ) близок, но работать с ним очень скользко. За исключением этого, объявление-время сделает этот шаблон чище.

...