Потоки с параметрами и ParameterizedThreadStart
Последнее обновление: 31.10.2015
В предыдущем примере мы рассмотрели, как запускать в отдельных потоках методы без параметров. А что, если нам надо передать какие-нибудь параметры в поток?
Для этой цели используется делегат ParameterizedThreadStart. Его действие похоже на функциональность делегата .
Рассмотрим на примере:
class Program { static void Main(string[] args) { int number = 4; // создаем новый поток Thread myThread = new Thread(new ParameterizedThreadStart(Count)); myThread.Start(number); for (int i = 1; i < 9; i++) { Console.WriteLine("Главный поток:"); Console.WriteLine(i * i); Thread.Sleep(300); } Console.ReadLine(); } public static void Count(object x) { for (int i = 1; i < 9; i++) { int n = (int)x; Console.WriteLine("Второй поток:"); Console.WriteLine(i*n); Thread.Sleep(400); } } }
После создания потока мы передаем метод переменную, значение которой хотим передать в поток.
При использовании ParameterizedThreadStart мы сталкиваемся с ограничением: мы можем запускать во втором потоке только такой метод, который в
качестве единственного параметра принимает объект типа . Поэтому в данном случае нам надо дополнительно привести переданное значение
к типу int, чтобы его использовать в вычислениях.
Но что делать, если нам надо передать не один, а несколько параметров различного типа? В этом случае на помощь приходит классовый подход:
class Program { static void Main(string[] args) { Counter counter = new Counter(); counter.x = 4; counter.y = 5; Thread myThread = new Thread(new ParameterizedThreadStart(Count)); myThread.Start(counter); //................... } public static void Count(object obj) { for (int i = 1; i < 9; i++) { Counter c = (Counter)obj; Console.WriteLine("Второй поток:"); Console.WriteLine(i*c.x *c.y); } } } public class Counter { public int x; public int y; }
Сначала определяем специальный класс Counter, объект которого будет передаваться во второй поток, а в методе Main передаем его во второй поток.
Но тут опять же есть одно ограничение: метод не является типобезопасным, то есть мы можем передать в него любой тип,
и потом нам придется приводить переданный объект к нужному нам типу. Для решения данной проблемы рекомендуется объявлять все используемые методы
и переменные в специальном классе, а в основной программе запускать поток через ThreadStart. Например:
class Program { static void Main(string[] args) { Counter counter = new Counter(5, 4); Thread myThread = new Thread(new ThreadStart(counter.Count)); myThread.Start(); //........................ } } public class Counter { private int x; private int y; public Counter(int _x, int _y) { this.x = _x; this.y = _y; } public void Count() { for (int i = 1; i < 9; i++) { Console.WriteLine("Второй поток:"); Console.WriteLine(i * x * y); Thread.Sleep(400); } } }
НазадВперед
7 ответов
Вы можете использовать DELEGET FUNC в .NET 3.5 в качестве параметра в вашем методе RunTheMethod. Делегат Func позволяет указывать метод, который принимает ряд параметров определенного типа и возвращает один аргумент определенного типа. Вот пример, который должен работать:
ответ дан 22 November 2019 в 21:49
Если Вы хотите передать Метод как параметр, используйте:
ответ дан 22 November 2019 в 21:49
второй класс является Клиентом, который будет использовать класс памяти. Это имеет Основной метод, который создает экземпляр PersonDB, и это называет это методом Процесса objectвЂs с методом, который определяется в Клиентском классе.
ответ дан 22 November 2019 в 21:49
Вам нужно использовать делегат . В этом случае все ваши методы принимают строку параметр и возврат — это наиболее просто представлено функцией делегатом 1 . Таким образом, ваш код может стать правильным с таким простым изменением, как это:
Делегаты имеют гораздо больше мощности, чем это, по общему признанию. Например, с C # вы можете создать делегат из выражения лямбда , поэтому вы можете вызвать ваш метод таким образом:
, который создаст анонимную функцию, подобную этой:
, а затем пройти этот делегат к метод.
Вы можете использовать делегаты для подписок событий, асинхронного исполнения, обратных вызовов — всевозможных вещей. Он стоит читать на них, особенно если вы хотите использовать LINQ. У меня есть статья , которая является в основном в о различиях между делегатами и событиями, но вы можете все равно найти его полезным.
1 Это только основывается на универсальном типа делегата в рамках; Вы можете легко заявить о своих собственных:
, а затем сделать параметр типа вместо этого.
ответ дан 22 November 2019 в 21:49
Вы должны использовать функцию , который представляет функцию, принимающую строку как аргумент и возврат :
Тогда используйте его:
ответ дан 22 November 2019 в 21:49
Вот пример без параметра:
http://en.charp-online.net/csharp_faq:_how_call_a_method_using_a_name_string
С params:
Вы в основном проходите в массиве объектов вместе с именем метода. Затем вы используете как с помощью метода Invoke.
Объект параметров [] Параметры
-121—1781603-
Если вы хотите, чтобы способность меняться, какой метод вызывается во время выполнения, я бы порекомендовал использовать делегат: http://www.codeproject.com/kb/cs/delegates_step1. ASPX
Это позволит вам создать объект для хранения метода для звонка, и вы можете пройти это к другим методам, когда он необходим.
ответ дан 22 November 2019 в 21:49
Вот пример без параметра:
http://en.charp-online.net/csharp_faq:_how_call_a_method_using_a_name_string
С params:
Вы в основном проходите в массиве объектов вместе с именем метода. Затем вы используете как с помощью метода Invoke.
Объект параметров [] Параметры
ответ дан 22 November 2019 в 21:49
Другие вопросы по тегам:
Predicate
Встроенный, нешаблонизированый тип делегата. Имеет 16 версий объявления начиная с .NET Framewok4.0. Как и в случае Func и Action версии делегата различаются количество входных параметров. Выходной или возвращаемое значение всегда одно, типа bool, поэтому назначения делегата – передавать ссылку на метод, в котором реализован алгоритм сравнения объектов.
По функциональности полностью идентичен Func<T, bool>. Однако введен в библиотеку типов по двум причинам: исторической, поскольку в языке появился раньше, чем универсальные типы; и из стремления к строгому типизированную в framework, для создания специального типа для каждого круга задач.
В предыдущем примере использован анонимный метод в качестве инициализации делегата Predicate<Т>, запись:
неявно приводится компилятором к созданию делегата, инициализации его методом. Никто, однако, не мешает сделать метод именованным и явно передать его в делегат Predicate.
По сути весь тяжкий выбор для программиста между Predicate<T> и Func<T, bool> лежит в области семантики: в случае Predicate метод на основании инструкций вернет true или false, а в случае Func<T,bool> тип bool, а это не всегда одно и то же.
Передача по ссылке и передача по значению
При передаче в метод объекта ссылочного типа передается ссылка на этот объект. То есть метод получает не сам объект, а аргумент, который указывает расположение объекта. При изменении члена объекта с помощью этой ссылки это изменение отражается в аргументе в вызывающем методе, даже если объект передается по значению.
Ссылочный тип создается с помощью ключевого слова , как показано в следующем примере.
Теперь, если передать объект, основанный на этом типе, в метод, то будет передана ссылка на объект. В следующем примере объект типа передается в метод :
В этом примере, в сущности, делается то же, что и в предыдущем примере, — аргумент по значению передается в метод. Но поскольку здесь используется ссылочный тип, результат будет другим. В данном случае в методе изменено поле параметра , а также изменено поле аргумента, в методе . В качестве выходных данных метод отображает 33.
Особенности передачи переменных различных типов данных по значению
Передача переменных различных типов данных по значению имеет свои особенности. Они заключаются в том, что при работе с методами и свойствами контекста формальных параметров может изменяться состояние фактического параметра, передаваемого при вызове процедуры или функции.
Пример 1:
Рассмотрим в качестве примера ситуацию, когда параметром процедуры, передаваемым по значению, является таблица значений. Внутри вызываемой процедуры ОбработатьТаблицу() создается новый экземпляр таблицы значений, в который добавляется три строки. Это никак не влияет на состояние таблицы значений Тз, переданной в качестве фактического параметра при вызове процедуры ОбработатьТаблицу():
// Указано, что передача параметра ТаблЗнач осуществляется по значению Процедура ОбработатьТаблицу(Знач ТаблЗнач) // Создадим новую таблицу значений ТаблЗнач = Новый ТаблицаЗначений; // Добавим в таблицу значений две колонки ТаблЗнач.Колонки.Добавить("Колонка1"); ТаблЗнач.Колонки.Добавить("Колонка2"); // Добавим несколько строк в таблицу значений Для п = 1 По 3 Цикл НовСтрока = ТаблЗнач.Добавить(); НовСтрока.Колонка1 = "Колонка"; НовСтрока.Колонка2 = п; КонецЦикла; КонецПроцедуры // Создадим новую таблицу значений Тз = Новый ТаблицаЗначений; // Добавим в таблицу значений три колонки Тз.Колонки.Добавить("Колонка1"); Тз.Колонки.Добавить("Колонка2"); Тз.Колонки.Добавить("Колонка3"); // Добавим несколько строк в таблицу значений Для п = 1 По 10 Цикл НовСтрока = Тз.Добавить(); НовСтрока.Колонка1 = "Колонка1"; НовСтрока.Колонка2 = "Колонка2"; НовСтрока.Колонка3 = п; КонецЦикла; // будет выведено значение 10 Сообщить("Число элементов в ТЗ до обработки: " + Тз.Количество()); // Вызовем процедуру, передав фактический параметр Тз по значению ОбработатьТаблицу(Тз); // будет выведено значение 10: внутри процедуры ОбработатьТаблицу // в формальный параметр сохранили новую таблицу значений Сообщить("Число элементов в ТЗ после обработки: " + Тз.Количество());
Пример 2:
Рассмотрим в качестве примера ситуацию, когда параметром процедуры, передаваемым по значению, является таблица значений. Внутри вызываемой процедуры СвернутьТаблицу() таблица значений, переданная в качестве параметра при вызове процедуры, сворачивается:
// Указано, что передача параметра ТаблЗнач осуществляется по значению Процедура СвернутьТаблицу(Знач ТаблЗнач) // Свернем таблицу значений по колонкам Колонка1 и Колонка3 ТаблЗнач.Свернуть("Колонка1", "Колонка3"); КонецПроцедуры // Создадим новую таблицу значений Тз = Новый ТаблицаЗначений; // Добавим в таблицу значений три колонки Тз.Колонки.Добавить("Колонка1"); Тз.Колонки.Добавить("Колонка2"); Тз.Колонки.Добавить("Колонка3"); // Добавим несколько строк в таблицу значений Для п = 1 По 10 Цикл НовСтрока = Тз.Добавить(); НовСтрока.Колонка1 = "Колонка1"; НовСтрока.Колонка2 = "Колонка2"; НовСтрока.Колонка3 = п; КонецЦикла; // будет выведено значение 10 Сообщить("Число элементов в ТЗ до сворачивания: " + Тз.Количество()); // Вызовем процедуру, передав фактический параметр Тз по значению СвернутьТаблицу(Тз); // Таблица значений теперь свернута: будет выведено значение 1 Сообщить("Число элементов в ТЗ после сворачивания: " + Тз.Количество());
Схематично изобразить происходящее при вызове процедуры СвернутьТаблицу(Тз) можно следующим образом:
(1) — вызов процедуры СвернутьТаблицу. В качестве фактического параметра указывается переменная Тз, хранящая ссылку на экземпляр таблицы значений;
(2) — из процедуры вызывается метод Свернуть таблицы значений;
(3) — формальный параметр ссылается на тот же самый экземпляр таблицы значений, что и фактический параметр (переменная Тз) — происходит свертка в экземпляре таблицы значений, на которую ссылается переменная Тз;
(4) — выходим из процедуры. Экземпляр таблицы значений, на которую ссылается переменная Тз, находится в свернутом состоянии.
Таким образом, передача фактического параметра по значению в данном случае не привела к созданию копии таблицы значений. При работе со свойствами и методами таблицы значений мы работаем со свойствами и методами той таблицы значений, которая использовалась при вызове процедуры СвернутьТаблицу().
Методы расширения
Как правило, добавлять методы в существующий тип можно двумя способами:
- Изменение исходного кода для этого типа. Конечно, если вы не владеете исходным кодом этого типа, сделать это невозможно. Если при этом в поддержку метода также добавляются поля закрытых данных, это изменение становится критическим.
- Определение нового метода в производном классе. Нельзя добавить метод этим способом, используя наследование для других типов, таких как структуры и перечисления. Кроме того, оно не позволяет «добавить» метод в запечатанный класс.
Методы расширения позволяют «добавить» метод в существующий тип, не меняя сам тип и не реализуя новый метод в наследуемом типе. Кроме того, метод расширения может не входить в ту же сборку, в которую входит расширяемый им тип. Вызовите метод расширения, как будто он является определенным членом типа.
Дополнительные сведения см. в статье Методы расширения.
Ковариантность и контравариантность делегатов
Последнее обновление: 13.10.2018
Делегаты могут быть ковариантными и контравариантными. Ковариантность делегата
предполагает, что возвращаемым типом может быть более производный тип. Контрвариантность
делегата предполагает, что типом параметра может быть более универсальный тип.
Ковариантность
Ковариантность позволяет возвращать из метода объект, тип которого является производным от типа, возвращаемого
делегатом.
Допустим, имеется следующая структура классов:
class Person { public string Name { get; set; } } class Client : Person { }
В этом случае ковариантность делегата может выглядеть следующим образом:
delegate Person PersonFactory(string name); static void Main(string[] args) { PersonFactory personDel; personDel = BuildClient; // ковариантность Person p = personDel("Tom"); Console.WriteLine(p.Name); Console.Read(); } private static Client BuildClient(string name) { return new Client {Name = name}; }
То есть здесь делегат возвращает объект Person. Однако благодаря ковариантности данный делегат может указывать на метод,
который возвращает объект производного типа, например, Client.
Контрвариантность
Контрвариантность предполагает возможность передавать в метод объект, тип которого является более универсальным по отношению к типу параметра
делегата. Например, возьмем выше определенные классы Person и Client и используем их в следующем примере:
delegate void ClientInfo(Client client); static void Main(string[] args) { ClientInfo clientInfo = GetPersonInfo; // контравариантность Client client = new Client{Name = "Alice"}; clientInfo(client); Console.Read(); } private static void GetPersonInfo(Person p) { Console.WriteLine(p.Name); }
Несмотря на то, что делегат в качестве параметра принимает объект Client, ему можно присвоить метод, принимающий в качестве параметра объект базового типа Person.
Может показаться на первый взгляд, что здесь есть некоторое противоречие, то есть использование более универсального тип вместо более
производного. Однако в реальности в делегат при его вызове мы все равно можем передать только объекты типа Client, а любой объект типа Client является объектом типа Person, который используется в методе.
Ковариантность и контравариантность в обобщенных делегатах
Обобщенные делегаты также могут быть ковариантными и контравариантными, что дает нам больше гибкости в их использовании.
Например, возьмем следующую иерархию классов:
class Person { public string Name { get; set; } public virtual void Display() => Console.WriteLine($"Person {Name}"); } class Client : Person { public override void Display() => Console.WriteLine($"Client {Name}"); }
Теперь объявим и используем ковариантный обобщенный делегат:
class Program { delegate T Builder<out T>(string name); static void Main(string[] args) { Builder<Client> clientBuilder = GetClient; Builder<Person> personBuilder1 = clientBuilder; // ковариантность Builder<Person> personBuilder2 = GetClient; // ковариантность Person p = personBuilder1("Tom"); // вызов делегата p.Display(); // Client: Tom Console.Read(); } private static Person GetPerson(string name) { return new Person {Name = name}; } private static Client GetClient(string name) { return new Client {Name = name}; } }
Благодаря использованию out мы можем присвоить делегату типа делегат типа
или ссылку на метод, который возвращает значение Client.
Рассмотрим контравариантный обобщенный делегат:
class Program { delegate void GetInfo<in T>(T item); static void Main(string[] args) { GetInfo<Person> personInfo = PersonInfo; GetInfo<Client> clientInfo = personInfo; // контравариантность Client client = new Client { Name = "Tom" }; clientInfo(client); // Client: Tom Console.Read(); } private static void PersonInfo(Person p) => p.Display(); private static void ClientInfo(Client cl) => cl.Display(); }
Использование ключевого слова in позволяет присвоить делегат с более универсальным типом (GetInfo<Person>)
делегату с производным типом (GetInfo<Client>).
Как и в случае с обобщенными интерфейсами параметр ковариантного типа применяется только к типу значения, которые возвращается делегатом. А
параметр контравариантного типа применяется только к входным аргументам делегата.
НазадВперед
Асинхронные методы
С помощью функции async можно вызывать асинхронные методы, не прибегая к использованию явных обратных вызовов или ручному разделению кода между несколькими методами или лямбда-выражениями.
Если пометить метод с помощью модификатора async , можно использовать в этом методе инструкцию await . Когда управление достигает выражения await в асинхронном методе, управление возвращается вызывающему объекту и выполнение метода приостанавливается до завершения выполнения ожидающей задачи. После завершения задачи можно возобновить выполнение в методе.
Примечание
Асинхронный метод возвращается в вызывающий объект, когда он встречает первый ожидаемый объект, выполнение которого еще не завершено, или когда выполнение асинхронного метода доходит до конца — в зависимости от того, что происходит раньше.
Асинхронный метод обычно имеет тип возвращаемого значения Task<TResult>, Task, IAsyncEnumerable<T> или . Тип возвращаемого значения в основном используется для определения обработчиков событий, где требуется возвращать тип . Асинхронный метод, который возвращает тип , не может быть ожидающим. Вызывающий объект метода, возвращающего значение типа void, не может перехватывать исключения, которые выдает этот метод. Начиная с C# 7.0 асинхронный метод может возвращать .
В следующем примере является асинхронным методом с типом возврата Task<TResult>. имеет инструкцию , которая возвращает целое число. Поэтому объявление метода должно иметь тип возврата . Поскольку тип возврата — , вычисление выражения в создает целое число, как показывает следующая инструкция: .
В следующем примере метод служит примером асинхронного метода с типом возврата Task. Он переходит к методу и, поскольку он выражается в одной строке, он может опустить ключевые слова и . Поскольку является асинхронным методом, задача для вызова должна быть ожидаемой, как показывает следующая инструкция: .
Асинхронный метод не может объявить все параметры ref или out , но может вызывать методы, которые имеют такие параметры.
Дополнительные сведения об асинхронных методах см. в разделах Асинхронное программирование с использованием ключевых слов async и await (C#) и Типы возвращаемых значений асинхронных операций.
Действие в коде
В нашем сценарии нам нужно вызвать метод с произвольным , который поступает из пользовательского ввода. Итак, давайте определим функцию, которая будет обслуживать эту функцию. Мы назовем это :
Он получает , объект в нашем случае и для проверки свойства , как в вопросе.
Наш объект является поддельным классом, который имитирует семантику вопроса:
Прежде всего, нам нужно найти метод по имени. Поскольку это единственный метод, названный таким образом (без перегрузок), мы можем смело использовать :
Но мы не можем вызвать его напрямую, так как он принимает не только аргументы, но и общие параметры. Мы можем предоставить их, предоставив для метода отражения:
И тогда мы готовы к этому без аргументов (поскольку он не принимает никаких), но нам нужно указать объект, из которого он будет вызываться (в нашем случае ):
Круто до сих пор, есть драконы, чтобы прийти сюда, потому что теперь нам нужно построить лямбду. Метод из расширений (на самом деле это статический метод в System.Linq.Enumerable) получает вместо , поэтому нам нужно создать его :
Это строит например тип с двумя общими параметрами. Первый — это переданный тип, а второй — логический, имитирующий предикат по функции.
Теперь нам нужно построить левую часть лямбды, сделав параметр данного типа:
И получая из него поле им:
И правая часть выражений — это имя, с которым мы будем сравнивать:
Построение всей лямбды — это следующий шаг. Из типа предиката (, выражения равенства и параметра предиката «i»):
Теперь нам нужно найти метод . Он находится в классе, содержащем все методы расширения, а не в интерфейсе :
Но это универсальный метод, получающий тип из входных данных, поэтому мы должны сделать это также:
Поскольку это статический метод, объект должен быть передан в качестве аргумента (что касается семантики метода расширения). Первым аргументом в массиве объектов является интерфейс расширения (, например, тип возврата ), а вторым аргументом является вышеприведенная лямбда-компиляция:
И здесь мы остановимся на примере. Вам нужно будет настроить его для вашего случая и добавить вызовы других методов. Это очень многословно и безобразно, но будет работать для любых объектов, содержащих поле . В более крупных сценариях, если вы не привязываете к определенному полю жестко закодированное, это будет работать для самых разных входов. Так же, как фреймворки работают с нашим кодом.
Полный пример вы можете найти ниже:
10 ответов
Лучший ответ
Есть несколько способов сделать это возможным, хотя я бы никому из них не советовал.
Во-первых, вы можете использовать отражение, что означает, что вам нужно написать много дополнительного (подверженного ошибкам) кода в методе , чтобы получить нужные значения.
Во-вторых, если вам известны параметры анонимного типа, которые вы передаете своему методу, вы можете использовать описанную технику здесь. Вы можете привести к другому анонимному типу внутри вашего метода, который имеет те же свойства. Здесь — еще одно описание той же техники от Джона Скита.
В-третьих, вы можете использовать классы из . Например, это используется в ASP.NET MVC. Он использует отражение под капотом. Однако в ASP.NET MVC имена свойств либо хорошо известны (например, и ), либо их имена не имеют значения, потому что они передаются как есть в метод контроллера ( например).
9
Ronald Wildenberg
7 Авг 2018 в 07:14
В конце концов я наткнулся на этот пост, демонстрирующий использование анонимных типов в качестве словарей. Используя этот метод, вы можете передать анонимный тип в качестве параметра метода (объекта) и получить доступ к его свойствам.
Однако я бы также добавил, что после изучения новых динамических функций в .net 4.0, таких как ExpandoObject, стало намного удобнее передавать динамический объект в качестве параметра:
21
Ben Foster
6 Сен 2010 в 21:47
Используйте динамический объект для параметров, если вы хотите передать анонимный тип. Метод выполнения плагина должен ожидать определенные свойства объекта параметра для работы. При использовании динамического ключевого слова компилятор C # получит указание не выполнять проверку типа параметра и позволит использовать строго типизированный синтаксис в коде плагина. Разрешение имен свойств будет происходить во время выполнения, и если переданный объект не имеет таких свойств, будет выдано исключение.
12
Alex T.
6 Ноя 2011 в 00:28
В этом примере анонимный объект преобразуется в словарь:
Вы можете назвать это так:
7
Pavel Chuchuva
13 Авг 2012 в 05:57
Если это автономный тип из Linq, вы можете легко сделать это, передав IEnumerable.
Вот пример метода получения
Конечно, поскольку вы используете отражение, вы можете увидеть проблемы с производительностью на более медленных машинах или там, где у вас либо большой IEnumerable, либо много свойств в T.
3
William
24 Сен 2012 в 22:18
Я сделал это однажды. Что вы можете сделать, так это получить параметры, ожидаемые от функции, через отражение. Затем вы можете создать свой массив параметров, сопоставив имена в массиве параметров с ключами анонимного объекта.
Надеюсь, это поможет :-).
1
Alxandr
8 Авг 2010 в 10:04
1
jvinhit
8 Июл 2014 в 10:29
Прежде всего, проверьте пространство имен System.Addin, там вам может помочь.
Во-вторых, вы можете создать собственный интерфейс с определенным именем метода и параметрами и позволить плагину реализовать интерфейс. Вы можете определить интерфейс плагина в другом проекте, на который можно ссылаться как в приложении, так и в проекте плагина.
Akash Kava
8 Авг 2010 в 10:13
С С # 7.0 вы можете использовать кортежи. https://docs.microsoft.com/en-us/dotnet/csharp/ кортежи
Вот пример:
Надеюсь, это поможет.
SzymonB
21 Мар 2020 в 19:09
Привет, я написал об этом сообщение:
Надеюсь, это поможет.
2
Jorge Fioranelli
23 Июн 2011 в 02:22
Необязательные параметры и аргументы
В определении метода может быть указано, являются его параметры обязательными или нет. По умолчанию параметры обязательны. Для определения необязательных параметров значения параметра по умолчанию включаются в определение метода. Если при вызове метода никакие аргументы для необязательного параметры не указываются, вместо них используется значение по умолчанию.
Значение параметра по умолчанию должно быть назначено одним из следующих видов выражений:
Константа, например, строковый литерал или число.
Выражение, имеющее вид , где может быть либо типом значения, либо ссылочным типом. Использование ссылочного типа практически эквивалентно указанию . Начиная с C# 7.1 вы можете использовать литерал , поскольку компилятор может выводить тип из объявления параметра.
Выражение в форме , где — это тип значения
Обратите внимание на то, что при этом вызывается не имеющий параметров неявный конструктор типа значения, который не является фактическим членом типа.
Примечание
В C# 10 и более поздних версиях, когда выражение формы вызывает явно определенный конструктор без параметров типа значения, компилятор выдает ошибку, так как значение параметра по умолчанию должно быть константой времени компиляции. Используйте выражение или литерал для предоставления значения параметра по умолчанию
Дополнительные сведения о конструкторах без параметров см. в разделе статьи Типы структур.
Если метод содержит как обязательные, так и необязательные параметры, необязательные параметры определяются в конце списка параметров после всех обязательных параметров.
В следующем примере определяется метод , который имеет один обязательный и два необязательных параметра.
Если для вызова метода с несколькими необязательными аргументами используются позиционные аргументы, вызывающий объект должен предоставить аргумент для всех необязательных параметров, для которых предоставлен аргумент, от первого до последнего. Например, если при использовании метода вызывающий объект предоставляет аргумент для параметра , он должен также предоставить его для параметра . — допустимый вызов метода; вызывает ошибку компилятора «Аргумент отсутствует».
Если метод вызывается с помощью именованных аргументов или комбинации позиционных и именованных аргументов, вызывающий объект может опустить любые аргументы, следующие за последним позиционным аргументом в вызове метода.
В следующем примере метод вызывается трижды. В первых двух вызовах метода используются позиционные аргументы. В первом пропускаются оба необязательных аргумента, а во втором — последний. Третий вызов метода предоставляет позиционный аргумент для обязательного параметра, но использует именованный аргумент для передачи значения в параметр , в то время как аргумент опускается.
Использование необязательных параметров влияет на разрешение перегрузки или на способ, с помощью которого компилятор C# определяет, какая именно перегрузка должна вызываться при вызове метода, следующим образом:
- Метод, индексатор или конструктор является кандидатом на выполнение, если каждый из его параметров необязателен либо по имени или позиции соответствует одному и тому же аргументу в операторе вызова, и этот аргумент можно преобразовать в тип параметра.
- Если найдено более одного кандидата, правила разрешения перегрузки для предпочтительных преобразований применяются к аргументам, указанным явно. Опущенные аргументы для необязательных параметров игнорируются.
- Если два кандидата определяются как равно подходящие, предпочтение отдается кандидату без необязательных параметров, аргументы которых в вызове были опущены. Это — последовательность определения приоритетов в разрешении перегрузки для кандидатов с меньшим числом параметров.