Страницы

Поиск по вопросам

среда, 22 января 2020 г.

Генерация заготовок для экземпляра класса

#c_sharp #visual_studio #resharper


Есть Visual Studio 2017 и последний Resharper.

Есть какой-нибудь класс с десятком полей типа:

public class MyRequest
{
    public int ID { get; set; }

    public string Inn { get; set; }

    public string Kpp { get; set; }

    public int  YetAnotherParam { get; set; }

    ....
}


И нужно бывает быстро что-то проверить, для чего открываешь проект с юнит-тестами
и тоскливо начинаешь набирать:

var request = new MyRequest
{
    ID = 1,
    Inn = "",
    Kpp = ...


А нельзя ли чтобы не отвлекаться на эту портянку полей -- просто нажать какой-то
решарперный или студийный хоткей на классе и получить заготовку, в которой уже можно
сразу подставлять более реальные параметры.

Есть ли какие-то способы облегчить себе жизнь или только вручную?
    


Ответы

Ответ 1



А зачем нам решарпер? Мыжпрограммисты! Давайте сваяем кодогенерацию. Вот вспомогательный класс: class InitializerGenerator { public static string Generate() { var type = typeof(T); var className = type.Name; var varName = char.ToLowerInvariant(className[0]) + className.Substring(1); var result = $"var {varName} = {GenerateTypeLiteral(type, 0)};"; // Requires STA thread System.Windows.Clipboard.SetText( result, System.Windows.TextDataFormat.UnicodeText); // PresentationCore.dll (WPF) // Requires STA thread //System.Windows.Forms.Clipboard.SetText( // result, // System.Windows.Forms.TextDataFormat.UnicodeText); // System.Windows.Forms.dll return result; } static Dictionary specialLiterals = new Dictionary() { [typeof(char)] = @"'\0'", [typeof(float)] = "0.0f", [typeof(double)] = "0.0", [typeof(bool)] = "false", [typeof(decimal)] = "0", [typeof(string)] = "\"\"", }; const int indentSize = 4; static string GenerateTypeLiteral(Type type, int indentLevel) { if (specialLiterals.TryGetValue(type, out var s)) return s; if (type.IsPointer) return "null"; if (type.IsPrimitive) return "0"; var propertyIndent = new string(' ', (indentLevel + 1) * indentSize); var propertytLines = new List(); foreach (var p in type.GetProperties()) { if (!p.CanWrite) continue; var propertyType = p.PropertyType; var value = GenerateTypeLiteral(propertyType, indentLevel + 1); propertytLines.Add($"{propertyIndent}{p.Name} = {value}"); } var className = type.Name; var constructor = $"new {className}()"; if (propertytLines.Count == 0) return constructor; var indent = new string(' ', indentLevel * indentSize); var rb = new StringBuilder(); rb.AppendLine(constructor); rb.AppendLine($"{indent}{{"); rb.AppendLine(string.Join($",{Environment.NewLine}", propertytLines)); rb.Append($"{indent}}}"); return rb.ToString(); } } Его можно положить в дальний угол проекта, и при необходимости вызвать с нужными параметрами. Он даже, если хотите, поместит сгенерированный код в буфер обмена (очень удобно, кстати). Пользоваться так: (Определения классов) public class MyRequest { public int ID { get; set; } public string Inn { get; set; } public Inner Inner { get; set; } public string Kpp { get; set; } public int YetAnotherParam { get; set; } public Inner2 Inner2 { get; set; } } public class Inner { public int ID { get; set; } } public class Inner2 { public int ID { get; } } (Код) InitializerGenerator.Generate(); Результат: var myRequest = new MyRequest() { ID = 0, Inn = "", Inner = new Inner() { ID = 0 }, Kpp = "", YetAnotherParam = 0, Inner2 = new Inner2() }; Недостатки: не генерирует параметры конструктора, исходит из существования конструктора по умолчанию (так что нужный конструктор придётся вызывать вручную). Можно было бы добавить и это, но непонятно, что делать в случае наличия нескольких конструкторов.

Комментариев нет:

Отправить комментарий