Я работаю над проектом, в котором мне нужно определить любое количество возможных параметров при создании объекта.
public class base
{
//Variable Declarations here...
public base(){ //Handle Arguments here... }
}
public class customCommand : base
{
//Action Logic here...
}
Моя основная проблема связана с объявлениями «customCommand» (извините за плохие соглашения об именах). Если пользователь хочет создать объект, который не передает никаких дополнительных параметров, тогда объявление работает отлично. Однако если пользователь хочет создать объект, который передает параметр, мне нужно обработать это с помощью нового объявления. Цель сценария — позволить пользователю создать команду для симулятора командной строки-esc. У меня есть «интерпретатор», который будет передавать любые параметры в метод, связанный с командой. Мой интерпретатор уже «обрабатывает» безопасность типов, возвращая пользователю любую ошибку, возвращаемую назначенной функцией, в отформатированном виде. Если пользователю нужно ввести «command1 [строка] [int]», а он набирает «command1 [строка] [строка]», например, интерпретатор увидит, что назначенная функция вернула ошибку типа вместе с необходимым типом, и попросите пользователя повторить команду с правильным синтаксисом.
Я бы определил команду 1, создав такой объект:
private static customCommand<string,int> command1;
и я бы определил метод, который использует команда (для интерпретатора), следующим образом:
command1 = new customCommand<string,int>((x,y) =>
{
//some method
});
public class customCommand<T1> : base
{
//New Action Logic here (handles T1)...
}
Я понимаю, что одним из решений этой проблемы является простое ограничение возможности пользователя создавать объект с количеством параметров 'n' и запись новых определений new с параметрами <T1> * n
каждый, например:
public class customCommand<T1> : base { }
public class customCommand<T1,T2> : base { }
public class customCommand<T1,T2,T3> : base { } etc...
однако возможные входные данные пользователя могут превышать 10-11 записей, что не только будет затруднительно управлять 10-11 различными объявлениями, но также будет служить беспорядочным решением.
Я попытался создать класс с пустым списком параметров. Я ожидал, что смогу создать объект, в который смогу передать любое количество необходимых мне параметров.
public class customCommand<new T[]> { };
private static customCommand<string, string, int> test; //parameters are just an example
Буду рад любой помощи, я все еще учусь. Спасибо! (извините за плохое форматирование, я пытаюсь отредактировать сообщение для комментариев во время работы be
Нет, такого способа нет. Например. встроенный в .NET Делегат Action имеет 17 перегрузок
I need to define any number of possible parameters
почему? Какую реальную проблему можно решить? Зачем использовать даже три типа вместо одного с тремя свойствами? Если класс должен что-то делать, почему бы не использовать Action<>
или Func<>
?
В C# есть ключевое слово params, которое может вам подойти (я использую его очень редко, поэтому у меня мало опыта работы с ним, извините). Learn.microsoft.com/en-us/dotnet/csharp/language-reference/… По той же логике вы можете просто передать массив в свой конструктор или объект контекста.
Параметры @topsail предназначены только для вызовов методов.
«Почему» имеет значение. В функциональном языке, где, например, часто встречаются кортежи разных типов, произвольные типы обрабатываются путем вложения кортежей, а не с помощью бесконечных параметров. Рекурсивный функциональный код, обрабатывающий два типа, может обрабатывать бесконечное число, поскольку второй тип сам может быть кортежем. Такой код легко написать на C# и использовать приемы, заимствованные из функциональных языков, например мемоизацию, для упрощения кода и уменьшения или даже полного устранения стоимости рекурсии.
@panagiotis-kanavos хорошая мысль. Наверное, я никогда не думал об объединении свойств в одно. Спасибо за предложение.
Я просто использовал параметры в методе конструктора :) Никаких ошибок.
Мое предложение состояло в том, чтобы объяснить реальную проблему, а не слияние. Хотя с record
объединить несколько значений теперь очень просто.
@topsail это вообще не имеет никакого отношения к этому вопросу. Вопрос в параметрах универсального типа, а не в параметрах метода.
окей, не беспокойся - я сейчас воздержусь от этого :) Я, наверное, просто невнимательно читал, я думал, что он просто хочет передать конструктору переменное количество аргументов.
@ Панайотис-Канавос О. Полагаю, я неправильно прочитал ваш ответ. Я хочу, чтобы пользователь мог создать объект с любым количеством параметров. Затем я хочу взять каждый параметр и передать его в сценарий моего интерпретатора. Я создаю модульную программу командной строки-esc и хочу, чтобы пользователь мог создавать свои собственные команды, к которым может получить доступ и которые будет понятен моему интерпретатору. Надеюсь это поможет. Спасибо!
@PanagiotisKanavos ну, это может быть частью ответа. Кажется, что OP использует дженерики для предоставления данных классу, а дженерики на самом деле не для этого.
Сказав это: как бы вы использовали такой объект. Представьте, что вы могли бы сделатьnew MyWeirdType(1, "hello", DateTume.Now)
. Как бы вы использовали такой объект, например. передать его другой функции? Вообще говоря, дженерики — это способ сказать «класс…», например, «a List<Apple>
— это список яблок». Однако Warrior<Sword>
не соответствует этому соглашению, поскольку «воин меча» — это буквально нонсенс.
@MakePeaceGreatAgain, предположения, лежащие в основе этих комментариев, неверны. Функциональные языки уже работают таким образом, и от этого зависит сам C#, начиная с LINQ. Для удобства Action<> имеет до 17 параметров, но можно легко обрабатывать больше, используя ValueTuple<>
для большего количества параметров. В C# использовать относительно просто: myAction((1,("banana",DateTime.Now()))
но F# устраняет необходимость явной вложенности. В C# 12 псевдонимы типов можно использовать для присвоения красивого имени любому кортежу.
Точно. Объект передается моему интерпретатору, который имеет возможность использовать любое количество параметров, проходя по ним. Проблема, с которой я сталкиваюсь, заключается в том, что я могу определить объект с таким количеством параметров, которое хочет пользователь. Я думаю, что попытаюсь запросить у пользователя одну запись, которая содержит все необходимые ему параметры, но объединена.
@Zacc: Вам следует попытаться изменить свой вопрос и привести лучшие примеры. Эти base
и new1
занятия просто сбивают с толку. Люди не смогут подсказать вам хороший способ, если не понимают, о чем он. Так что не скрывайте это в своих комментариях, а отредактируйте свой вопрос. Я бы просто передал экземпляр InterpreterArguments
в Interpreter
и обработал их там.
Почему бы просто не использовать Dictionary<TKey, object>
или просто List<object>
и повторить это в своем интерпретаторе? Конечно, вы теряете безопасность типов. Но вы все равно делаете это во время итерации, даже с дженериками.
Это была моя точная мысль. Мой интерпретатор уже «обрабатывает» безопасность типов, возвращая пользователю любую ошибку, возвращаемую назначенной функцией, в отформатированном виде. Если пользователю нужно ввести «command1 [строка] [int]», а он набирает «command1 [строка] [строка]», например, интерпретатор увидит, что назначенная функция вернула ошибку типа вместе с необходимым типом, и попросите пользователя повторить команду с правильным синтаксисом.
Но дженерики вообще не решат эту проблему, поскольку это проблема времени компиляции. Однако ваш интерпретатор выполняет все эти проверки во время выполнения. Поэтому просто используйте коллекцию, как указано выше.
Я думаю, что взаимодействие с переводчиком — хороший вариант использования Reflection. Так что просто ставьте object[]
Обратите внимание, что универсальные типы, кортежи и анонимные типы не являются динамическими. Они закрепляются во время компиляции. Если ваш сценарий динамичен (например, зависит от переменных, вводимых пользователем), используйте Dictionary<K, V>
или List<KeyValuePair<Type, object>>
, List<MyParameterDescription>
или просто List<object>
.
Я хочу, чтобы пользователь мог создать объект с любым количеством параметров. Затем я хочу взять каждый параметр и передать его в сценарий моего интерпретатора. Я создаю модульную программу командной строки-esc и хочу, чтобы пользователь мог создавать свои собственные команды, к которым может получить доступ и которые будет понятен моему интерпретатору.
Я бы предложил думать вместо одного аргумента, который может представлять несколько значений; варианты, которые приходят на ум:
// dictionary
var val = new Dictionary<string, object>
{
{ "Id", 42 },
{ "Name", "abc" },
};
SomeMethod(val);
// anonymous type, using reflection magic in your library
var val = new { Id = 42, Name = "abc" };
SomeMethod(val);
// named value-tuple; note names aren't available via reflection,
// but are via "analyzers"
var val = (id: 42, name: "abc");
SomeMethod(val);
// unnamed value-tuple
var val = (42, "abc");
SomeMethod(val);
// POCO type, using reflection magic in your library
var val = new SomeType { Id = 42, Name = "abc" };
SomeMethod(val);
и именно здесь ваша библиотека разрывает на части; другой подход состоит в том, чтобы иметь некоторую пару типов - например IParameterHandler<T>
, то есть для типа Foo
аргумента вы можете запросить IParameterHandler<Foo>
, который предоставляет вызывающий объект, который делает это в коде вызывающего объекта, извлекая некоторое количество типизированных аргументов.
Представьте, что существует способ определить такой класс:
class MyClass<T1, ..., Tn> { ... }
Как бы вы использовали этот класс? Представьте, что вы хотели поместить это в функцию. Как будет выглядеть подпись функции?
DoSomething(MyClass<hmmmm, what arguments do I need here? T1? T2? T38?> args) { ... }
Но даже если бы это сработало, как бы вы использовали этот объект в методе? Все, что вы потенциально можете сделать, это перебрать все эти значения. Но вы понятия не имеете, что на самом деле представляют собой все эти типы, так что же означает T38
? И есть ли у args
столько аргументов? Например. как бы вы написали такой цикл:
foreach((hmmm, which type do I need here? T1?, T30? Both?) m in args)
{
...
}
Вы застряли на обозначении каждого отдельного объекта как object
, и в этом случае вы полностью теряете всю безопасность типов, которую вы на самом деле хотели.
foreach(object m in args)
{
...
}
Даже сам класс не может использовать эти аргументы. Я полагаю, вы хотите использовать эти значения в качестве хранилища данных:
class MyClass<T1, ..., Tn>
{
public T1 FirstValue;
public T2 SecondValiue;
// how many properties do I actually have?
}
Однако, имея дело с любым произвольным количеством аргументов типа, как сам класс узнает, «сколько свойств ему нужно создать»?
Итак, в конце концов, дженерики, которые дают вам типы времени компиляции, не являются правильной вещью, когда вы хотите интерпретировать типы во время выполнения.
Вместо этого вы можете просто записать все эти значения в коллекцию, например. a List<object>
и повторите это в своем интерпретаторе:
class MyClass
{
public object[] Values { get; }
public MyClass(params object[] args) { this.Values = args; }
}
Теперь вы можете предоставить классу любое произвольное количество аргументов:
var m = new MyClass("Hello", 2, new MyClass());
Вашему интерпретатору просто нужно перебрать массив args
:
foreach(var o in m.Values)
{
...
}
Я понимаю, что теоретическое «бесконечное» количество потенциальных аргументов может быть проблематичным. Мне не удалось найти никакой отраслевой документации, подтверждающей идею о том, что в командной строке есть ограничение на количество аргументов, но, оглядываясь назад, я полагаю, что это имеет смысл. Обращаясь к решению, хотя мой интерпретатор обрабатывает типы, передаваемые аргументами, так же, как и любой другой интерпретатор, я обрабатываю типы объектов перед отправкой их интерпретатору, что может быть полезно для ошибок/выходных данных. Как вы сказали, я помещу аргументы в список и переработаю интерпретатор. Спасибо!
«Есть ли способ создать класс без ограничений на количество параметров универсального типа» Нет. Если вы создаете метод, скажем, с более чем тремя общими аргументами, вы, вероятно, поступаете неправильно. Это хороший признак проблемы X/Y.