Работа со вложенными Generic - типами

460
01 февраля 2017, 23:33

Доброго времени суток.

Есть Класс.

/// <summary>
/// Класс, являющийся "Номер Телефона".
/// </summary>
/// <typeparam name="T">Тип, в котором хранится Тип Номера Телефона.</typeparam>
// Кстати string не может быть ограничителем.
public class TelNumber<T> where T : string 
{
    /// <summary>
    /// ID Номер Телефона.
    /// </summary>
    public int TelNumberID { get; set; }
    /// <summary>
    /// Тип Номер Телефона.
    /// </summary>
    public TypeTelNumber<int,T> TypeTelNumber { get; set; }
}

Я хочу, чтобы тип телефона мог быть мог быть либо string, либо TypeTelNumber<I,T>.

Пишу так:

/// <summary>
/// Класс, являющийся "Номер Телефона".
/// </summary>
/// <typeparam name="T">Тип, в котором хранится Тип Номера Телефона.</typeparam>
public class TelNumber<T> where T : string, TypeTelNumber<I,T>
{
    /// <summary>
    /// ID Номер Телефона.
    /// </summary>
    public int TelNumberID { get; set; }
    /// <summary>
    /// Тип Номер Телефона.
    /// </summary>
    public T TypeTelNumber { get; set; }
}

Вопрос: Ругается на I, мол не такого типа (понятно и логично). Но как тогда сделать задуманное?

Answer 1

Ваш код where T : string означает, что Т может быть только string, поскольку класс System.String объявлен как sealed.

Чтобы скомпилировалось, достаточно объявить параметр I:

public class TelNumber<I, T> where T : string, TypeTelNumber<I,T>
{
    /// <summary>
    /// ID Номер Телефона.
    /// </summary>
    public int TelNumberID { get; set; }
    /// <summary>
    /// Тип Номер Телефона.
    /// </summary>
    public T TypeTelNumber { get; set; }
}

Но это не то, что вам нужно: в C# нет discriminated union-типов. Код where T : string, TypeTelNumber<I,T> означает, что T должен быть одновременно и string, и TypeTelNumber<I,T>. А поскольку string есть sealed, это означает, что ваш код не скомпилируется ни с каким типом.

Имеет смысл объявить просто два отдельных класса, реализующих общий интерфейс.

interface class ITelNumber<T>
{
    /// <summary>
    /// ID Номер Телефона.
    /// </summary>
    int TelNumberID { get; set; }
    /// <summary>
    /// Тип Номер Телефона.
    /// </summary>
    T TypeTelNumber { get; set; }
}
class StringTelNumber : ITelNumber<string>
{
    public int TelNumberID { get; set; }
    public string TypeTelNumber { get; set; }
}
class CustomTelNumber<I, T> : ITelNumber<TypeTelNumber<I, T>>
{
    public int TelNumberID { get; set; }
    public TypeTelNumber<I, T> TypeTelNumber { get; set; }
}

А ограничения на типы налагать уже вашей бизнес-логикой, а не системой типов: в C# система типов такого (пока) не умеет.

Answer 2

Как ответили в комментариях, пока задуманного сделать нельзя, ибо мой первый кусок означает И, а не ИЛИ.

Вариант 1

Сделать, как в моем варианте.

Вариант 2

Не ставить ограничений, т.е

/// <summary>
/// Класс, являющийся "Номер Телефона".
/// </summary>
/// <typeparam name="T">Тип, в котором хранится Тип Номера Телефона.</typeparam>
public class TelNumber<T> 
{
    /// <summary>
    /// ID Номер Телефона.
    /// </summary>
    public int TelNumberID { get; set; }
    /// <summary>
    /// Тип Номер Телефона.
    /// </summary>
    public T TypeTelNumber { get; set; }
}

Во втором варианте появляется проблема, что я не могу контролировать какой тип используется для хранения типа. Ну и ладно((

Вариант 3

Сделать так, как предложил VladD в ответе. Именно это вариант я и использую.

READ ALSO
Class Library для локальной базы Microsoft Access

Class Library для локальной базы Microsoft Access

У меня будут два проекта которые будут взаимодействовать(CRUD) с одной локальной базой данных(mdb)Как правильно это реализовать? DataSet, OleDbDataAdapter-ы,...

352
Настройка компилятора c#

Настройка компилятора c#

Здравствуйте, я тут пытаюсь сделать так, что бы после компиляции не создавался файл exe, а выводилось сразу все в консоль(те

468
Devexpress: repositoryitemtextedit и маска

Devexpress: repositoryitemtextedit и маска

Есть у меня GridControl от DevexpressДля одного column этого грида задан columnedit : repositoryitemtextedit

389
Для чего нужны дженерик параметры в объявлении делегата, если все типы входных и выходных параметров строго определены?

Для чего нужны дженерик параметры в объявлении делегата, если все типы входных и выходных параметров строго определены?

Такой вопрос, для чего в объявлении делегата в данном примере нужны дженерик параметры <Т1, Т2> если их нет в конъюнктуре метода на который...

670