Написать класс для switch?
Написать класс для switch?
Дано:
класс TModel.
public class Model{private const string _R33 = "R33";private const string _R34 = "R34";
private readonly string ModelValue;
private Model(string pModelValue){ModelValue = pModelValue;}public static Model R33 = new Model(_R33);public static Model Р34 = new Model(_R34);....
}
помимо указанного еще имплементируются операции сравнения... и немножко функций.
Все с целью возможности написания в коде:
Model currentModel = null;
if(currentMode == Model.R33) { ... }
все хорошо, все работает как должнО.
Однако есть дополнительная потребность использовать currentModel в операторе switch.
Т.е. хочется что-то вида
switch(currentModel)
{
case Model.R33:
....
break;
case Model.R34:
....
break;
}
Что-то у меня никак это не получается. Есть идеи как сделать?
У тебя как всегда жесть :)
У switch'а в case'е должна быть константа.
А так, добавляешь оператор
public static implicit operator string(Model val) { return val.ModelValue; }
и все работает:
switch (currentModel) { case "R33": break; case "R34": break; }
У тебя как всегда жесть :)
-----
Ну а зачем по-другому? :)
У switch'а в case'е должна быть константа.
-----
Разумеется.
Я могу даже для простоты поменять доступ на паблик к _Rxx-констанстантам в классе для case TModel._R33:
Но решение, которое меня интересует, заключается в использовании инстанса класса TModel в качестве константы.
Т.е. интересует именно так, как написано - case TModel.R33:
Ну не хватает мне в шарпе плюснотого помещающего синтаксиса...
Это все, разумеется, прочитано и понятно.
Остальные ограничения, как:
- енум не имеет функций
- статический элемент не является константой
- инстансе класса не является константой
- и т.п.
Все - понятно.
НО! Все, что представляет собой требуемый класс является или может быть представлено, типом инт/стринг с дополнительной функциональностью.
Вопрос - как обойти? - разумеется, сохранив нужный формат записи.
Проще говоря - Как сказать системе, что R33 & R34 есть суть константы расширенного типа инт/стринг?
Базис для этого есть - енум.
НО! Все, что представляет собой требуемый класс является или может быть представлено, типом инт/стринг с дополнительной функциональностью.
Ну так и имплицитно кастуй к инту/стрингу/енуму. switch это скушает.
А вот в case будь любезен записать константу.
Либо переходишь на C# 7.0 и пишешь что-то вроде такого (New C# 7 Features - Is Expression With Patterns And Switch...):
switch (currentModel) { case R33 r33 when (r33.ModelValue == "R33"): break; case R34 r34 when (r34.ModelValue == "R34"): break; }
А вот в case будь любезен записать константу.
-----
Ты просто внимания не обратил - оно и есть, по сути, константа. Только типа TModel, но без проблем совместимая с string/int.
То, что меня интересует - как это оформить...
case R33 r33 when (r33.ModelValue == "R33"):
скорее что-то вида:
case TModel currentModel when (currentModel == TModel.R33):
но что-то выглядит слишком сложно - можно и ифы аккуратно прописать...
Потому снова возвращаемся к простому вопросу - как описать константу некоего класса?
Ты просто внимания не обратил - оно и есть, по сути, константа. Только типа TModel, но без проблем совместимая с string/int.
Это константа только для тебя. Для всех остальных это ни секунды не константа.
Чтобы понять, что это не константа, просто скажи мне, как должен реагировать switch, если ты напишешь так:
public static Model R33 = new Model(_R33); public static Model Р34 = new Model(_R33);
?
Если бы это были бы константы, то при компилировании кода:
switch(currentModel) { case Model.R33: .... break; case Model.R34: .... break; }
ты должен был бы получить ошибку.
Я бы с удовольствием написал:
public const TModel R33 = _R33;но не понимаю (не знаю, не умейu) как определить класс ТМодел чтобы это было возможно.
В этом и есть вопрос.
A constant expression is an expression that can be fully evaluated at
compile time. Therefore, the only possible values for constants of
reference types are
string
and a null reference.
Так что ответ на твой вопрос - никак.
A constant expression is an expression that can be fully evaluated at compile time.
-----
Ты полагаешь, что Я этого не понимаю?
Понимаю и вполне отчетливо - не зря же Я упомянул помещающий синтаксис из плюсов - он как раз позволяет инициировать константы определяемых типов. Да, они полностью определяются при компиляции константированием обьекта.
Для решения проблемы в данном случае необходимо сделать две вещи:
- научится размещать в области хранения констант готовый к применению объект класса TModel. Можно ли его там разместить - не знаю - данные - точно можно, объекты стринг/инт - тоже. Т.е. в общем случае - можно. Вопрос - как.
- научить свитч понимать TModel, как инт/стринг. Практика синонимов - int == Int32 - и енумов в шарпе есть - т.е. проблема потенциально решаемая. Вопрос тот же - КАК?
Мне так кажется, что надо рыть вот тут:
https://docs.microsoft.com/en-us/dotnet/api/system.int32?v...
В плане - struct... и аттрибутов.
Здравствуйте,
Предлагаю сделать так:
abstract class ModelBase : System.Object {
public ModelBase(): base() { }
}
class Model : ModelBase {
public enum ModelType { R33, R34, R35 };
protected ModelType _modelType;
public ModelType getModelType() { return _modelType; }
public Model(ModelType modelType) : base() { _modelType = modelType; }
}
class ModelR33 : Model {
public ModelR33() : base( Model.ModelType.R33 ) { }
}
class ModelR34 : Model
{
public ModelR34() : base(Model.ModelType.R34) { }
}
class ModelR35 : Model
{
public ModelR35() : base(Model.ModelType.R35) { }
}
class Program
{
static void Main(string[] args)
{
ModelR33 mR33 = new ModelR33();
ModelR34 mR34 = new ModelR34();
ModelR35 mR35 = new ModelR35();
Model currentModel = mR34;
switch ( currentModel.getModelType() ) {
case Model.ModelType.R33:
Console.WriteLine("Current Model is R33");
break;
case Model.ModelType.R34:
Console.WriteLine("Current Model is R34");
break;
case Model.ModelType.R35:
Console.WriteLine("Current Model is R35");
break;
}
}
}
Прислушайтесь к советам общественности и пересмотрите дизайн. Если есть необходимость именно так писать - что-то у Вас в коде (или в комманде) не на своем месте.
Успехов!
Murr'у так не надо. Murr'у надо через жопу :)
На самом деле ты просто заменил строковые константы Murr'а на enum :) Ну и зачем-то добавил структуру классов :)
Скорее всего Murr значительно упростил пример. На самом деле классы у него генерируется шаблонами T4 :) Да и switch этот тоже скорее всего генерируется автоматически. Ну а всех этих констант или енумов в сгенерированном описании класса просто нет :) Поэтому признак для переключения у него статические объекты :)
Ну а в шаблоне проще сгенерировать
switch (obj) { case XXX: break; case YYY: break; default: }
чем
if (obj == XXX) { } else if (obj == YYY) { } else { }
Другого объяснения у меня нет :)
что-то у Вас в коде (или в комманде) не на своем месте
-----
Что именно не так?
Возможность определять константы выбранного типа в Шарпе?
А чем именно это плохо? Я вот пока не вижу плохого.
сделать так:
-----
Избыточно и ничего принципиально нового.
То, что меня интересует:
public const ModelR33 r33 = Model.ModelType.R33.
Скорее всего Murr значительно упростил пример.
-----
Да, Я убрал все лишнее - чтобы ничего не мешало понять что именно нужно.
генерируется шаблонами T4
-----
В данном случае - нет.
в шаблоне проще сгенерировать
-----
В шаблоне без разницы что именно выдавать как результат - TModel._R32 или TModel.R32... if() или switch-case...
А вот путаться каждый раз в том что именно использовано - строковая константа или константа типа TModel - совершенно не хочется.
Хочется иметь константу и иметь правила для ее сравнения, отличные от стандартных.
Ох, я с самого начала неправильно понял в чем проблема, извините.
Как сделать то, что вы ищите я не знаю. Удивлюсь, если это окажется невозможным. Я пробывал с определением операторов приведения типов. Не работает так.
public static implicit operator Model( ModelType modelType) { return new Model(modelType); }
public static implicit operator ModelType ( Model model) { return model._modelType; }
В С++, как Вы сами заметили, все решается операторами приведения типов. Сейчас, говорят, можно и литервлы свои создавать (даже приводить не надо: https://en.cppreference.com/w/cpp/language/user_literal)
что-то у Вас в коде (или в комманде) не на своем месте -----
Что именно не так?
Возможность определять константы выбранного типа в Шарпе?
А чем именно это плохо? Я вот пока не вижу плохого.
Думал предложить поллиморфные вызовы, вместо switch. Но теперь понимаю, что проблема совсем не в этом. Недосмотрел.
class ModelR33 : Model{ public ModelR33() : base(Model.ModelType.R33) { }class ModelR34 : Model{ public ModelR34() : base(Model.ModelType.R34) { }