C>Какая-то очень мутная фича. Решение крайне узкой задачи, которую можно решить другими методами. C>Или я то-то упускаю?
Имхо, это очень простая фича. По-сути, это всего лишь возможность разбить код класса на несколько файлов в рамках одной сборки.
А прикладное применение из того, что вспомнилось:
1. Разделение объемного класса на несколько файлов для удобства восприятия и поддержки: по функциональности (My class.cs, MyClass.Feature1.cs, MyClass.Fearure2.cs), по платформе (My class.cs, MyClass.net4x.cs, MyClass.netcore.cs), по каким-то иным критериям. Причем, конкретные файлы могут включаться в проект по каким-то условиям.
2. Расширение функционала автоматически сгенерированных классов без необходимости наследования (e.g EFCore db first, кодогенераторы API-клиентов по swagger/avro/protobuf спекам)
3. Кодогенерация реализации partial методов, объявленных пользователем (см. например, Riok.Mapperly).
C>А нафига, собственно, без необходимости наследования?
Ну а зачем заводить лишний уровень абстракции, если можно без него обойтись?
Скажем, в случае автогенерации HTTP-клиента, это может выглядеть вот так:
// Client.generated.cspartial class ApiClient {
public async Task SendRequest(HttpRequestMessage request) {
OnBeforeRequest(request);
// .. логика отправки запроса, получение ответа
OnAfterResponse(response);
}
partial void OnBeforeRequest(HttpRequestMessage request);
partial void OnAfterResponse(HttpRequestMessage response);
}
// И это дает возможность простой кастомизации запросов/ответов
// Client.Customization.cspartial class ApiClient {
partial void OnBeforeRequest(HttpRequestMessage request) {
request.Headers.Add("X-Header", "SomeValue");
}
}
Или взять другой пример — редактор форм в WinForms/WPF.
// MainWindow.xaml.cspublic partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
// Здесь располгается наш код (обработчики событий и т.п.)
}
// MainWindow.g.i.cs - автосгенерированый файлpublic partial class MainWindow : System.Windows.Window, System.Windows.Markup.IComponentConnector {
// Здесь куча автосгенерированного кода по созданию, инициализации, настройке, позиционированиию компонентов,
// которые мы мышкой накидали в визуальном редакторе
}
Здравствуйте, Codealot, Вы писали:
C>Здравствуйте, RushDevion, Вы писали:
RD>>Ну а зачем заводить лишний уровень абстракции, если можно без него обойтись?
C>Пц. А это ничего, что ради этого бзика пришлось заводить целую новую фичу языка, которая вероятно будет нужна 0.0001% рарзработчиков?
У нас в проекте есть, сами сделали зачем-то. А так когда одна часть класса автогенерированна, а вторая пишется программистом — то нормально.
Здравствуйте, Qulac, Вы писали:
Q>А так когда одна часть класса автогенерированна, а вторая пишется программистом — то нормально.
Да не, у меня другие вопросы.
1. А что, им никак нельзя было обойтись без поддержки в языке?
2. Какому числу людей вообще нужна эта фича? Сколько людей пишет кодогенерацию на C#, на весь мир? 10 тысяч? Думаю, намного меньше.
C>Пц. А это ничего, что ради этого бзика пришлось заводить целую новую фичу языка, которая вероятно будет нужна 0.0001% рарзработчиков?
Ну, если верить https://dotnetcrunch.in/csharp-version-history-features, то patial types появились в C# 2.0, а partial methods в C# 3.0.
И, как мне кажется, не последнюю роль в этом сыграли как раз WinForms и ASP.NET Web forms, где появилась насущная потребность разделять автогенерируемую и пользовательскую части классов.
А дальше с появлением source generator'ов эта фича вышла на новый уровень:
// Генерация кода конечного автомата на C#
[GeneratedRegex("cat|dog", RegexOptions.IgnoreCase, "en-US")]
private static partial Regex CatOrDogGeneratedRegex();
// Генерация кода маппинга
[Mapper]
public partial class CarMapper
{
public partial CarDto CarToCarDto(Car car);
}
Здравствуйте, Codealot, Вы писали:
RD>>появилась насущная потребность разделять автогенерируемую и пользовательскую части классов.
C>Тот же вопрос. Эту задачу нельзя было решить без специальной поддержки в языке?
А у меня встречный вопрос: почему ты вдруг взвился именно на эту фичу?
По-моему, она достаточно безобидная.
А так — можно было и без этого, конечно. Можно было через #include заставить всё реально собираться в одном файле. Можно было вообще всех заставить писать напрямую на ассемблере Где граница?
Здравствуйте, netch80, Вы писали:
N>А у меня встречный вопрос: почему ты вдруг взвился именно на эту фичу?
Я уже написал две причины.
N>А так — можно было и без этого, конечно. Можно было через #include заставить всё реально собираться в одном файле. Можно было вообще всех заставить писать напрямую на ассемблере Где граница?
Не доводи до абсурда. Можно легко решить эту задачу простейшими средствами языка.
Здравствуйте, Codealot, Вы писали:
N>>А у меня встречный вопрос: почему ты вдруг взвился именно на эту фичу?
C>Я уже написал две причины.
Я перечитал всю тему и не понял, какие это "две причины". Ты говорил, что оно нужно <очень малой доле> разработчиков. Сочту это за половину причины. Где ещё одна?
Повтори, пожалуйста, а то понять таки сложно.
N>>А так — можно было и без этого, конечно. Можно было через #include заставить всё реально собираться в одном файле. Можно было вообще всех заставить писать напрямую на ассемблере Где граница?
C>Не доводи до абсурда. Можно легко решить эту задачу простейшими средствами языка.
RD>>появилась насущная потребность разделять автогенерируемую и пользовательскую части классов.
C>Тот же вопрос. Эту задачу нельзя было решить без специальной поддержки в языке?
А предложи как именно это сделать без поддержки в языке на примере того же WinForms.
public partial class MainWindow : Window
{
public MainWindow()
{
// В австосгенеренной partial-части создается код вида
// private Button _button1;
//
// _button1 = new Button();
// _button1.Position = new Position(...);
InitializeComponent();
}
// Здесь располгается наш код (обработчики событий и т.п.)
}
Как сделать то же самое, не нарушая инкапсуляцию класса MainWindow (т.е. не отдавая наружу все эти private _button1)
и не вводя лишние сущности, вроде MainWindowInitializer и т.п.?
Как при этом сделать так, чтобы пользовательский код не ломал автосгенеренный и наоборот?
Начнем с того, что это у тебя не partial methods.
RD>Как сделать то же самое, не нарушая инкапсуляцию класса MainWindow (т.е. не отдавая наружу все эти private _button1)
Делаешь их protected, генеришь базовый класс, наследуешься от него.
RD>и не вводя лишние сущности, вроде MainWindowInitializer и т.п.?
Почему нет, собственно? Опять сектантские поверья?
C>Вот не надо петросянить. Вопрос был про одну фичу, а ты придумал про другую, хотя и связанную.
Так это ж не я придумал. Это эти... которые из микрософта.
А я просто теоретизирую, как исторически могла возникнуть необходимость в partial types.
А partial methods, имхо, запилили чисто за компанию. Типа, а чё прикольно и сделать несложно. А давайте прикрутим? А давайте!
Здравствуйте, RushDevion, Вы писали:
RD>А я просто теоретизирую, как исторически могла возникнуть необходимость в partial types.
Про типы еще более-менее, хотя все равно фича довольно сомнительная.
RD>А partial methods, имхо, запилили чисто за компанию. Типа, а чё прикольно и сделать несложно. А давайте прикрутим? А давайте!
Здравствуйте, Codealot, Вы писали:
C>Или я то-то упускаю?
Класс можно вынести в шаред проджект, подключить его к другим проектам, в которых сделать свою реализацию partial methods специфичную для данной сборки.