Как использовать Наследование при использовании Общих Ограничений - PullRequest
7 голосов
/ 02 сентября 2010

Я испытываю трудности с общими ограничениями при попытке реализовать библиотеку, которая позволяет наследовать, и надеюсь, что кто-то может помочь.

Я пытаюсь создать библиотеку классов, в которой есть 3 разновидности, каждое из которых строится поверх другого. Мне показалось, что это прекрасная возможность использовать Дженерики, поскольку я не могу делать то, что хочу, через чистое наследование. Код ниже (он должен быть вставлен прямо в VS) с некоторыми пояснениями:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Test
{
    #region Base Classes

    public class GenericElement { }

    /// <summary>Visit to a GenericElement</summary>
    public class Generic_Visit<E> where E : GenericElement
    {
        public E Element { get; set; }
    }

    /// <summary>Collection of Visits</summary>
    public class Generic_Route<V, E>
        where V : Generic_Visit<E>
        where E : GenericElement
    {
        public List<V> Visits { get; set; }
        public Double Distance { get; set; }
    }

    /// <summary>Collection of Routes</summary>
    public class Generic_Solution<R, V, E>
        where R : Generic_Route<V, E>
        where V : Generic_Visit<E>
        where E : GenericElement
    {
        public List<R> Routes { get; set; }

        public Double Distance
        {
            get
            {
                return this.Routes.Select(r => r.Distance).Sum();
            }
        }
    }

    #endregion

    #region TSP Classes

    public class Concrete_TSPNode : GenericElement { }

    public abstract class Generic_TSPVisit<E> : Generic_Visit<E>
        where E : Concrete_TSPNode
    {
        public Double Time { get; set; }
    }

    public abstract class Generic_TSPRoute<V, E> : Generic_Route<V, E>
        where V : Concrete_TSPVisit
        where E : Concrete_TSPNode
    {
        public Double Time
        {
            get
            {
                return this.Visits.Select(v => v.Time).Sum();
            }
        }
    }

    public abstract class Generic_TSPSolution<R, V, E> : Generic_Solution<R, V, E>
        where R : Concrete_TSPRoute
        where V : Concrete_TSPVisit
        where E : Concrete_TSPNode
    {
        public Double Time
        {
            get
            {
                return this.Routes.Select(r => r.Time).Sum();
            }
        }
    }

    public class Concrete_TSPVisit : Generic_TSPVisit<Concrete_TSPNode> { }

    public class Concrete_TSPRoute : Generic_TSPRoute<Concrete_TSPVisit, Concrete_TSPNode> { }

    public class Concrete_TSPSolution : Generic_TSPSolution<Concrete_TSPRoute, Concrete_TSPVisit, Concrete_TSPNode> { }

    #endregion

    #region VRP

    public class Concrete_VRPNode : Concrete_TSPNode { }

    public abstract class Generic_VRPVisit<E> : Generic_TSPVisit<E> where E : Concrete_VRPNode
    {
        public Double Capacity { get; set; }
    }

    public abstract class Generic_VRPRoute<V, E> : Generic_TSPRoute<V, E>
        where V : Concrete_VRPVisit
        where E : Concrete_VRPNode
    {
        public Double Capacity
        {
            get
            {
                return this.Visits.Select(v => v.Capacity).Sum();
            }
        }
    }

    public abstract class G_VRPSolution<R, V, E> : Generic_TSPSolution<R, V, E>
        where R : Concrete_VRPRoute
        where V : Concrete_VRPVisit
        where E : Concrete_VRPNode
    {
        public Double Capacity
        {
            get
            {
                return this.Routes.Select(r => r.Capacity).Sum();
            }
        }
    }

    public class Concrete_VRPVisit : Generic_VRPVisit<Concrete_VRPNode> { }

    public class Concrete_VRPRoute : Generic_VRPRoute<Concrete_VRPVisit, Concrete_VRPNode> { }

    public class Concrete_VRPSolution : Generic_TSPSolution<Concrete_VRPRoute, Concrete_VRPVisit, Concrete_VRPNode> { }

    #endregion
}

Идея кода заключается в том, что существует набор базовых классов, которые предоставляют свойства с использованием Generics. Это позволяет мне иметь строго типизированные коллекции, например.

Затем на этом этапе строятся 2 из 3 этапов, TSP и VRP, в которых есть 4 конкретных класса (это то, с чем должен взаимодействовать разработчик, использующий библиотеку классов, так как общие ограничения просто сходят с ума) - Элемент, Визит, Маршрут и Решение.

Есть также некоторые классы с префиксом Generic для TSP и VRP. Они позволяют наследование, которое я хочу, поскольку оно раскрывает общие типы. Если я не использую их (скажем, Concrete_VRPRoute наследует Concrete_TSPRoute), то мне придется продолжать приводить тип элемента, возвращаемого коллекцией Visits, чтобы получить, например, свойство Capacity.

Я вполне уверен, что все типы выстроены правильно, но когда я пытаюсь собрать, я получаю следующие ошибки, и я действительно не знаю, как их решить.

Ошибка 1 Тип «V» нельзя использовать как введите параметр 'V' в универсальном типе или метод «Test.Generic_Route». Там нет неявной ссылки преобразование из V в 'Test.Generic_Visit.

Ошибка 2 тип 'V' не может использоваться как тип параметр 'V' в универсальном типе или метод "Test.Generic_Solution". Там нет неявной ссылки преобразование из V в 'Test.Generic_Visit.

Ошибка 3 тип 'R' не может быть использован как тип параметр 'R' в универсальном типе или метод "Test.Generic_Solution". Там нет неявной ссылки преобразование из 'R' в 'Test.Generic_Route'

Ошибка 4 Тип «V» нельзя использовать как введите параметр 'V' в универсальном типе или метод 'Test.Generic_TSPRoute. Есть нет неявного преобразования ссылок из От V до Test.Concrete_TSPVisit.

Ошибка 5 Тип «V» нельзя использовать как введите параметр 'V' в универсальном типе или метод 'Test.Generic_TSPSolution. Там нет неявной ссылки преобразование из V в 'Test.Concrete_TSPVisit.

Ошибка 6 тип 'R' не может быть использован как тип параметр 'R' в универсальном типе или метод 'Test.Generic_TSPSolution. Там нет неявной ссылки преобразование из 'R' в 'Test.Concrete_TSPRoute.

Ошибка 7 тип "Test.Concrete_VRPVisit" не может использоваться в качестве параметра типа 'V' в универсальный тип или метод 'Test.Generic_TSPSolution. Там нет неявной ссылки преобразование из 'Test.Concrete_VRPVisit' для 'Test.Concrete_TSPVisit.

Ошибка 8 тип "Test.Concrete_VRPRoute" не может использоваться в качестве параметра типа «R» в универсальный тип или метод 'Test.Generic_TSPSolution. Там нет неявной ссылки преобразование из "Test.Concrete_VRPRoute" для 'Test.Concrete_TSPRoute'. 'Test.Concrete_TSPRoute'.

Ответы [ 3 ]

6 голосов
/ 02 сентября 2010

Это кусок общего торта.Вам необходимо определить общие классы с точки зрения их самих.Рекурсивное универсальное определение.

Базовые классы:

public class Generic_Element<E>
    where E : Generic_Element<E>
{
}

/// <summary>Visit to a Generic_Element</summary>
public class Generic_Visit<V, E>
    where V : Generic_Visit<V, E>
    where E : Generic_Element<E>
{
    public E Element { get; set; }
}

/// <summary>Collection of Visits</summary>
public class Generic_Route<R, V, E>
    where R : Generic_Route<R, V, E>
    where V : Generic_Visit<V, E>
    where E : Generic_Element<E>
{
    public List<V> Visits { get; set; }
    public Double Distance { get; set; }
}

/// <summary>Collection of Routes</summary>
public class Generic_Solution<S, R, V, E>
    where S : Generic_Solution<S, R, V, E>
    where R : Generic_Route<R, V, E>
    where V : Generic_Visit<V, E>
    where E : Generic_Element<E>
{
    public List<R> Routes { get; set; }

    public Double Distance
    {
        get
        {
            return this.Routes.Select(r => r.Distance).Sum();
        }
    }
}

Классы TSP:

public class Generic_Tsp_Element<E> : Generic_Element<E>
where E : Generic_Tsp_Element<E>
{
}

/// <summary>Visit to a Generic_Element</summary>
public class Generic_Tsp_Visit<V, E> : Generic_Visit<V, E>
    where V : Generic_Tsp_Visit<V, E>
    where E : Generic_Tsp_Element<E>
{
    public Double Time { get; set; }
}

/// <summary>Collection of Visits</summary>
public class Generic_Tsp_Route<R, V, E> : Generic_Route<R, V, E>
    where R : Generic_Tsp_Route<R, V, E>
    where V : Generic_Tsp_Visit<V, E>
    where E : Generic_Tsp_Element<E>
{
    public Double Time
    {
        get
        {
            return this.Visits.Select(v => v.Time).Sum();
        }
    }
}

/// <summary>Collection of Routes</summary>
public class Generic_Tsp_Solution<S, R, V, E> : Generic_Solution<S, R, V, E>
    where S : Generic_Tsp_Solution<S, R, V, E>
    where R : Generic_Tsp_Route<R, V, E>
    where V : Generic_Tsp_Visit<V, E>
    where E : Generic_Tsp_Element<E>
{
    public Double Time
    {
        get
        {
            return this.Routes.Select(r => r.Time).Sum();
        }
    }
}

public class Concrete_Tsp_Element : Generic_Tsp_Element<Concrete_Tsp_Element> { }

public class Concrete_Tsp_Visit : Generic_Tsp_Visit<Concrete_Tsp_Visit, Concrete_Tsp_Element> { }

public class Concrete_Tsp_Route : Generic_Tsp_Route<Concrete_Tsp_Route, Concrete_Tsp_Visit, Concrete_Tsp_Element> { }

public class Concrete_Tsp_Solution : Generic_Tsp_Solution<Concrete_Tsp_Solution, Concrete_Tsp_Route, Concrete_Tsp_Visit, Concrete_Tsp_Element> { }

Классы VRP:

public class Generic_Vrp_Element<E> : Generic_Element<E>
where E : Generic_Vrp_Element<E>
{
}

/// <summary>Visit to a Generic_Element</summary>
public class Generic_Vrp_Visit<V, E> : Generic_Visit<V, E>
    where V : Generic_Vrp_Visit<V, E>
    where E : Generic_Vrp_Element<E>
{
    public Double Capacity { get; set; }
}

/// <summary>Collection of Visits</summary>
public class Generic_Vrp_Route<R, V, E> : Generic_Route<R, V, E>
    where R : Generic_Vrp_Route<R, V, E>
    where V : Generic_Vrp_Visit<V, E>
    where E : Generic_Vrp_Element<E>
{
    public Double Capacity
    {
        get
        {
            return this.Visits.Select(v => v.Capacity).Sum();
        }
    }
}

/// <summary>Collection of Routes</summary>
public class Generic_Vrp_Solution<S, R, V, E> : Generic_Solution<S, R, V, E>
    where S : Generic_Vrp_Solution<S, R, V, E>
    where R : Generic_Vrp_Route<R, V, E>
    where V : Generic_Vrp_Visit<V, E>
    where E : Generic_Vrp_Element<E>
{
    public Double Capacity
    {
        get
        {
            return this.Routes.Select(r => r.Capacity).Sum();
        }
    }
}

public class Concrete_Vrp_Element : Generic_Vrp_Element<Concrete_Vrp_Element> { }

public class Concrete_Vrp_Visit : Generic_Vrp_Visit<Concrete_Vrp_Visit, Concrete_Vrp_Element> { }

public class Concrete_Vrp_Route : Generic_Vrp_Route<Concrete_Vrp_Route, Concrete_Vrp_Visit, Concrete_Vrp_Element> { }

public class Concrete_Vrp_Solution : Generic_Vrp_Solution<Concrete_Vrp_Solution, Concrete_Vrp_Route, Concrete_Vrp_Visit, Concrete_Vrp_Element> { }

Окончательный результатэто неуниверсальные конкретные классы, которые можно использовать так:

var e = new Concrete_Tsp_Element();
var v = new Concrete_Tsp_Visit();
v.Element = e;
v.Time = 0.5;
var r = new Concrete_Tsp_Route();
r.Visits = new List<Concrete_Tsp_Visit>(new[] { v });
r.Distance = 2.1;
var s = new Concrete_Tsp_Solution();
s.Routes = new List<Concrete_Tsp_Route>(new[] { r });
Console.WriteLine(s.Distance);
Console.WriteLine(s.Time);
Console.ReadLine();

Наслаждайтесь!Наслаждайтесь!

2 голосов
/ 02 сентября 2010

ОК, давайте рассмотрим первый.Ошибка:

Тип «V» нельзя использовать в качестве параметра типа «V» в универсальном типе или методе «Test.Generic_Route».Не существует неявного преобразования ссылок из 'V' в 'Test.Generic_Visit'.

Он жалуется на это объявление:

public abstract class Generic_TSPRoute<V, E> : Generic_Route<V, E>
    where V : Concrete_TSPVisit
    where E : Concrete_TSPNode

Это устанавливает два определения:

  • V является Concrete_TSPVisit (или его потомком)

  • E является Concrete_TSPNode (или его потомком)

Теперь давайте посмотрим, что Generic_Route<V, E> позволяет нам вставить:

public class Generic_Route<V, E>
    where V : Generic_Visit<E>
    where E : GenericElement

Второе ограничение хорошо, потому что Concrete_TSPNode - это GenericElement.Первый из них проблематичен: помните, что E является Concrete_TSPNode или его потомком , поэтому Generic_Visit<E> может быть:

  • Generic_Visit<Concrete_TSPNode>, или

  • Generic_Visit<some subclass of Concrete_TSPNode>

Однако мы также знаем, что ранее V является Concrete_TSPVisit (или его потомок).

  • Concrete_TSPVisit наследуется от Generic_TSPVisit<Concrete_TSPNode>

  • Generic_TSPVisit<Concrete_TSPNode> наследуется от Generic_Visit<Concrete_TSPNode>

Заметили что-нибудь?Это требует, чтобы это было Generic_Visit<Concrete_TSPNode>.Категорически недопустимо быть Generic_Visit<some subclass of Concrete_TSPNode>.

Другими словами, представьте себе, что я пишу это:

var route = new Generic_TSPRoute<Concrete_TSPVisit, Concrete_TSPNode_Subclass>();

Согласно вашей иерархии, Concrete_TSPVisit этоGeneric_Visit<Concrete_TSPNode> и, следовательно, имеет свойство, которое выглядит как

public Concrete_TSPNode Element { get; set; }

Если я получу значение из этого свойства, оно гарантированно будет Concrete_TSPNode, но не обязательно Concrete_TSPNode_Subclass.

РЕДАКТИРОВАТЬ:

Я оставлю этот ответ, потому что он объясняет причину ошибки компилятора, но ответ Enigmativity фактически обеспечивает решение проблемы.

0 голосов
/ 02 сентября 2010

Это то же самое, что и ответ Enigmativity, но со всеми лишними ограничениями.Он по-прежнему компилируется, в нем нет какой-либо общей рекурсии, и, насколько я могу судить, по-прежнему безопасен для типов, как и требовалось.Загадка, чего мне не хватает?:)

public class Generic_Element { }

public class Generic_Visit<E>
{
    public E Element { get; set; }
}

/// <summary>Collection of Visits</summary>
public class Generic_Route<V>
{
    public List<V> Visits { get; set; }
    public Double Distance { get; set; }
}

/// <summary>Collection of Routes</summary>
public class Generic_Solution<R, V>
    where R : Generic_Route<V>
{
    public List<R> Routes { get; set; }

    public Double Distance
    {
        get
        {
            return this.Routes.Select(r => r.Distance).Sum();
        }
    }
}

public class Generic_Tsp_Element : Generic_Element
{
}

/// <summary>Visit to a Generic_Element</summary>
public class Generic_Tsp_Visit<E> : Generic_Visit<E>
{
    public Double Time { get; set; }
}

/// <summary>Collection of Visits</summary>
public class Generic_Tsp_Route<V, E> : Generic_Route<V>
    where V : Generic_Tsp_Visit<E>
{
    public Double Time
    {
        get
        {
            return this.Visits.Select(v => v.Time).Sum();
        }
    }
}

/// <summary>Collection of Routes</summary>
public class Generic_Tsp_Solution<R, V, E> : Generic_Solution<R, V>
    where R : Generic_Tsp_Route<V, E>
    where V : Generic_Tsp_Visit<E>
{
    public Double Time
    {
        get
        {
            return this.Routes.Select(r => r.Time).Sum();
        }
    }
}

public class Concrete_Tsp_Element : Generic_Tsp_Element { }

public class Concrete_Tsp_Visit : Generic_Tsp_Visit<Concrete_Tsp_Element> { }

public class Concrete_Tsp_Route : Generic_Tsp_Route<Concrete_Tsp_Visit, Concrete_Tsp_Element> { }

public class Concrete_Tsp_Solution : Generic_Tsp_Solution<Concrete_Tsp_Route, Concrete_Tsp_Visit, Concrete_Tsp_Element> { }

public class Generic_Vrp_Element : Generic_Element
{
}

/// <summary>Visit to a Generic_Element</summary>
public class Generic_Vrp_Visit<V, E> : Generic_Visit<E>
{
    public Double Capacity { get; set; }
}

/// <summary>Collection of Visits</summary>
public class Generic_Vrp_Route<R, V, E> : Generic_Route<V>
    where V : Generic_Vrp_Visit<V, E>
{
    public Double Capacity
    {
        get
        {
            return this.Visits.Select(v => v.Capacity).Sum();
        }
    }
}

/// <summary>Collection of Routes</summary>
public class Generic_Vrp_Solution<S, R, V, E> : Generic_Solution<R, V>
    where R : Generic_Vrp_Route<R, V, E>
    where V : Generic_Vrp_Visit<V, E>
{
    public Double Capacity
    {
        get
        {
            return this.Routes.Select(r => r.Capacity).Sum();
        }
    }
}

public class Concrete_Vrp_Element : Generic_Vrp_Element { }

public class Concrete_Vrp_Visit : Generic_Vrp_Visit<Concrete_Vrp_Visit, Concrete_Vrp_Element> { }

public class Concrete_Vrp_Route : Generic_Vrp_Route<Concrete_Vrp_Route, Concrete_Vrp_Visit, Concrete_Vrp_Element> { }

public class Concrete_Vrp_Solution : Generic_Vrp_Solution<Concrete_Vrp_Solution, Concrete_Vrp_Route, Concrete_Vrp_Visit, Concrete_Vrp_Element> { }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...