C# - сделать в потомке дженерика параметр типа налловым
Скажем, есть базовый дженерик класс для общей обработки данных разных типов
class Bace<T> // нет ограничений на тип T
{
T Value { get; set; }
}
И унаследованный класс для обработки числовых данных - тут использую интерфейс INumber для ограничения на числа.
class Derived<T> : Base<T>
where T: INumber<T>
{
}Так вот, хочется не просто числа обрабатывать, а налловые числа. Для этого вычитал, что надо сделать ограничение ещё и на структуру
class Derived<T> : Base<T>
where T: struct, INumber<T>
{
}Тогда можно будет переопределить поле Value как налловое и работать в нём с налловыми числами
class Derived<T> : Base<T>
where T: struct, INumber<T>
{
new T? Value { get; set; }
}Как считаете, будет это работать?
Сразу второй вопрос. Вот есть в базовом классе общая обработка для всех типов, включая ссылочные. И хочется сохранить эту обработку и в производном классе, т.к. хотя там и налловая структура, но код по сути выглядит одинаково. Разница лишь в самом типе: T или T?. Скажем, есть метод с определённым кодом (неважно какой)
class Bace<T> // нет ограничений на тип T
{
T ProcessValue(T input) {...}
}Ну и в потомке хочу такой же по сути код, лишь сам параметр будет теперь налловой структурой, а не любым типом
class Derived<T> : Base<T>
where T: struct, INumber<T>
{
T? ProcessValue(T? input) {...} // как определить этот метод? через new, override, ещё как-то?
}Как сделать так, чтобы код не надо было повторять, заменяя лишь тип параметра? Ведь метод ProcessValue в потомковом классе, вызванный из базового класса, не может работать с параметрами типа T? с такими ограничениями на тип (у меня в Студии компилятор выдаёт ошибку - что-то типа о несовпадении сигнатур или типов параметров метода). Т.е. этот метод по сути переопределять придётся. Но т.к. код базового метода меня устраивает, я не хочу его копировать - хочу переиспользовать.
class Base<T>
{
protected T CoreProcess(T input)
{
// общий код
}
public T ProcessValue(T input) => CoreProcess(input);
}
----
class Derived<T> : Base<T>
where T : struct, INumber<T>
{
public T? ProcessValue(T? input)
{
if (input is null)
return null;
return CoreProcess(input.Value);
}
}
Тогда можно будет переопределить поле Value как налловое и работать в нём с налловыми числами
Это ужасное решение. Никогда не используй new для переопределения.
Если я правильно понял, то ты хочешь что-то такое:
class Derived<T> : Base<T?>
where T: struct, INumber<T>
{
}
Как сделать так, чтобы код не надо было повторять, заменяя лишь тип параметра?
Вариантов 2:
1) какпредложил
AlexNek.Это если у тебя совсем простая логика. Т.е. если на вход null, то и вернуть null.
Если логика сложнее, то есть и другой вариант.
2) Разница между T и T? - проверка на null поэтому ее имеет смысл вынести:
class Bace<T> // нет ограничений на тип T
{
protected virtual bool IsNull (T val)
{
return val is null;
}
public T ProcessValue(T input)
{
if (IsNull(input))
{
....
}
}
}class Derived<T> : Base<T?>
where T : struct, INumber<T>
{
protected override bool IsNull(T? val)
{
return !val.HasValue;
}
}Новые данные - я не могу менять базовый класс. По крайней мере сейчас нет - надо с главным говорить, а он либо сам это будет делать, либо потом мне дадут, но не сейчас. И только если будет время - там на самом деле всё сложнее и на этот базовый класс завязано несколько других классов, а я сейчас просто вычленил конкретную проблему, обрав всё остальное.
Сейчас нужно найти какое-то более быстрое решение, без сильного переписывания базового класса. А на данный момент в базовом нет методов типа CoreProcess, как предложил Алекснек.
Если я правильно понял, то ты хочешь что-то такое:
class Derived<T> : Base<T?> where T: struct, INumber<T> { }
Не это
Base
Вся суть
where T: struct
- чтобы можно было потом делать члены класса с типом T?
Хотел сделать универсальную обработку и для ссылочных типов, и для значений. Для этого в базовом классе есть обработка ProcessValue с типом T без ограничений на тип. Это то, что мне досталось. Теперь надо сделать тип, который таким же образом обрабатывает только числа. Я решил унаследоваться от базового класса и просто использовать его метод ProcessValue, но сделать дополнительное ограничение на тип, чтобы были возможны только числа:
where T: INumber<T>
Ну и потом добавилось требование - налловые числа:
where T: struct, INumber<T>
А сам код ProcessValue должен по сути остаться тем же - там весьма базовый функционал, который подходит под все типы. Но в таком наследовании его базовую версию нельзя вызвать из потомкового класса - см.
Если как-то поиграться с переопределениями и сигнатурами при наследовании нельзя, чтобы не переписывать базовый метод ProcessValue, то у нас есть пока костыль попроще и побыстрее, а переписывать базовый класс будем позже.
Забыл лишь написать, что ProcessValue может быть объявлен виртуальным в базовом классе - это можно сделать сейчас.
Пусть у тебя есть класс:
class Bace<T> // нет ограничений на тип T
{
public T ProcessValue(T input) {...}
}Делаешь так:
class Derived<T> : Base<T?>
where T: struct, INumber<T>
{
}После этого:
var num = new Derived<int>(); var processedVal = num.ProcessValue(5);
тип у processedVal будет T?.
А в этом случае
var num1 = new Base<int>(); var processedVal1 = num1.ProcessValue(5);
тип у processedVal1 будет T.
Ну или попробуй объяснить, какой тебе нужен результат :)
Вобщем, похоже, не получится сделать один универсальный параметр и передавать в него всё подряд, но при этом в одной из переписок (overriding) сделать ограничение на налловые числа - из-за фундаментальных ограничений https://stackoverflow.com/a/79156942/5015385
По-любому нужны два варианта метода, использущие тип значения и ссылочный тип в качестве параметра. Т.е. решать проблему одного и того же кода надо по-другому, а не простой перегрузкой одного метода. Может, вариант Алекснека подойдёт со специализированными методами, которые вызываются из одного универсального.

список