Patrick Skelton Ответов: 2

Как объявить и использовать функцию базового класса с параметром универсального типа?


Мой вопрос, я думаю, прекрасно иллюстрируется следующим кодом (который не компилируется в том виде, в каком он есть):

public abstract class MyBaseBusinessObjectClass
{
    public abstract string BusinessObjectName { get; }
}

public class BusinessObjectOne : MyBaseBusinessObjectClass
{
    public override string BusinessObjectName => "One";

    public string FunctionSpecificToOne => "1";
}

public class BusinessObjectTwo : MyBaseBusinessObjectClass
{
    public override string BusinessObjectName => "Two";

    public string FunctionSpecificToTwo => "2";
}


public abstract class MyBaseLogicClass
{
    public abstract void PrintParamName<T>( T param );
}

public class LogicHandlingOnes : MyBaseLogicClass
{
    public override void PrintParamName<BusinessObjectOne>( BusinessObjectOne param )
    {
        Console.WriteLine( $"BusinessObjectOne.PrintParamName() returned '{param.BusinessObjectName}'." );

        // The following line does not compile.
        string s = ((BusinessObjectOne)param).FunctionSpecificToOne();
    }
}

public class LogicHandlingTwo : MyBaseLogicClass
{
    public override void PrintParamName<BusinessObjectTwo>( BusinessObjectTwo param )
    {
        // The following line does not compile.
        Console.WriteLine( $"BusinessObjectTwo.PrintParamName() returned '{param.BusinessObjectName}'." );

        // The following line does not compile.
        string s = ((BusinessObjectTwo)param).FunctionSpecificToTwo();
    }
}


Это дает следующие ошибки при компиляции:

1>Program.cs(35,77,35,95): ошибка CS1061: 'BusinessObjectOne' не содержит определения для 'BusinessObjectName' и не может быть найден доступный метод расширения 'BusinessObjectName', принимающий первый аргумент типа 'BusinessObjectOne' (отсутствует ли директива using или ссылка на сборку?)
1>Program.cs(47,77,47,95): ошибка CS1061: 'BusinessObjectTwo' не содержит определения для 'BusinessObjectName' и не может быть найден доступный метод расширения 'BusinessObjectName', принимающий первый аргумент типа 'BusinessObjectTwo' (отсутствует директива using или ссылка на сборку?)
1>Program.cs(38,42,38,63): ошибка CS1061: 'BusinessObjectOne' не содержит определения для 'FunctionSpecificToOne' и не может быть найден доступный метод расширения 'FunctionSpecificToOne', принимающий первый аргумент типа 'BusinessObjectOne' (отсутствует ли директива using или ссылка на сборку?)
1>Program.cs(50,42,50,63): ошибка CS1061: 'BusinessObjectTwo' не содержит определения для 'FunctionSpecificToTwo' и не может быть найден доступный метод расширения 'FunctionSpecificToTwo', принимающий первый аргумент типа 'BusinessObjectTwo' (отсутствует директива using или ссылка на сборку?)

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

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

Есть ли что-то простое, чего я здесь не получаю, или мне нужно найти свою книгу " начало C#"?

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

Что я уже пробовал:

Я попытался ограничить универсальный тип следующим образом:

public abstract class MyBaseLogicClass
{
    public abstract void PrintParamName<T>( T param ) where T : MyBaseBusinessObjectClass;
}


Который избавляется от двух ошибок но оставляет:

1&ГТ;------ построение начато: проект: ConsoleApp1, конфигурация: debug любой ЦП ------
1>Program.cs(38,42,38,63): ошибка CS1061: 'BusinessObjectOne' не содержит определения для 'FunctionSpecificToOne' и не может быть найден доступный метод расширения 'FunctionSpecificToOne', принимающий первый аргумент типа 'BusinessObjectOne' (отсутствует ли директива using или ссылка на сборку?)
1>Program.cs(50,42,50,63): ошибка CS1061: 'BusinessObjectTwo' не содержит определения для 'FunctionSpecificToTwo' и не может быть найден доступный метод расширения 'FunctionSpecificToTwo', принимающий первый аргумент типа 'BusinessObjectTwo' (отсутствует директива using или ссылка на сборку?)

Richard MacCutchan

Это происходит потому, что вы определили FunctionSpecificToOne и FunctionSpecificToTwo как свойства, а не функции.

2 Ответов

Рейтинг:
8

F-ES Sitecore

Универсальный тип "T" определяется при создании класса, поэтому он должен перейти к определению класса

public abstract class MyBaseLogicClass<T>
{
    public abstract void PrintParamName(T param);
}

public class LogicHandlingOnes : MyBaseLogicClass<BusinessObjectOne>
{
    public override void PrintParamName(BusinessObjectOne param)
    {

        Console.WriteLine($"BusinessObjectOne.PrintParamName() returned '{param.BusinessObjectName}'.");

        string s = param.FunctionSpecificToOne;
    }
}

public class LogicHandlingTwo : MyBaseLogicClass<BusinessObjectTwo>
{
    public override void PrintParamName(BusinessObjectTwo param)
    {
        Console.WriteLine($"BusinessObjectTwo.PrintParamName() returned '{param.BusinessObjectName}'.");

        string s = param.FunctionSpecificToTwo;
    }
}


Вы также можете определить класс baselogic следующим образом

public abstract class MyBaseLogicClass<T> where T: MyBaseBusinessObjectClass


Возвращаясь к вашей первоначальной концепции, если универсальный тип определен для функции, то код, вызывающий функцию, определяет этот тип.

public abstract class MyBaseLogicClass<T>
{
    public abstract void PrintParamName(T param);

    public abstract void GenericFunction<R>(R param) where R: MyBaseBusinessObjectClass;
}

public class LogicHandlingOnes : MyBaseLogicClass<BusinessObjectOne>
{
    public override void GenericFunction<R>(R param)
    {
        // it is the creator that defines the type so we don't know what "R" is
        // but we know it has to inherit from MyBaseBusinessObjectClass so we can use
        // any methods available on MyBaseBusinessObjectClass

        Console.WriteLine(param.BusinessObjectName);
    }
}


код вызова;


LogicHandlingOnes ones = new LogicHandlingOnes();

// the calling code defines what "R" is
ones.GenericFunction<BusinessObjectOne>(new BusinessObjectOne()); // outputs "One"
ones.GenericFunction<BusinessObjectTwo>(new BusinessObjectTwo()); // outputs "Two"


Patrick Skelton

Я думаю, что этот "код, который вызывает функцию, определяет тип" является ключевой частью информации, которую я не смог понять.

Спасибо вам обоим!

Рейтинг:
16

phil.o

Вы могли бы определить MyBaseLogicClass как

public abstract class MyBaseLogicClass<T>
   where T : MyBaseBusinessObjectClass
{
   public abstract void PrintParamName( T param );
}

public class LogicHandlingOnes : MyBaseLogicClass<BusinessObjectOne>
{
   // ...
}

public class LogicHandlingTwo : MyBaseLogicClass<BusinessObjectTwo>
{
   // ...
}