Как сохранить все параметры ctor в полях - PullRequest
0 голосов
/ 22 февраля 2019

Я изучаю C #, и при кодировании возникла мысль.Можно ли автоматически сохранять параметры из конструктора в полях без необходимости писать this.var = var в каждой переменной для их хранения?

Пример:

class MyClass
{
    int var1;
    int var2;
    int var3;
    int var4;
    public MyClass(int var1, int var2, int var3, int var4){
        this.var1 = var1;
        this.var2 = var2;
        this.var3 = var3;
        this.var4 = var4;
    }
}

Есть лиспособ избежать записи this.varX = varX и сохранить все переменные в полях, если имена совпадают?

Ответы [ 8 ]

0 голосов
/ 01 марта 2019

Вы можете использовать «магию».

    class MyClass
    {
        int var1;
        int var2;
        int var3;
        int var4;
        public MyClass(int var1, int var2, int var3, int var4) => (this.var1, this.var2, this.var3, this.var4) = (var1, var2, var3, var4);
    }
0 голосов
/ 22 февраля 2019

Я могу подумать о нелегком способе заполнения полей объекта внутри конструктора, кроме как назначать их напрямую.Как уже говорили другие, могут быть некоторые обходные пути, использующие отражение, но это даже не близко к простоте заполнения полей вручную.

Может быть, один из способов заполнения полей класса из конструктора без добавления каких-либоФактический код для конструктора будет состоять в том, чтобы изменить IL после сборки и добавить код, который вы добавите вручную.Это может быть интересным подходом для библиотеки или пакета NuGet.

Кроме этого, я не вижу вариантов использования для заполнения полей класса из конструктора без их непосредственного назначения.Если у вас есть только несколько обязательных полей, конструктор довольно прост в реализации, поэтому он не будет стоить усилий.Если у вас огромный конструктор с большим количеством параметров, то это похоже на запах кода.Вы можете использовать Object Initializer , который довольно прост.

0 голосов
/ 22 февраля 2019

Нет, во многом потому, что эта модель довольно жесткая.Например, что, если вам нужен аргумент, который не будет установлен в поле;какой будет синтаксис для этого?Большинство решений, которые вы можете придумать, будут либо ограничивающими, либо почти столь же многословными, как и нынешний подход.

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

Итак, вы можете определить набор общих Tuple<> -подобных классов, которые вы можете наследовать от:

public abstract class TupleBase<T1>
{
    public T1 Argument1 { get; private set; }

    public TupleBase(T1 argument1)
    {
       this.Argument1 = argument1;
    }
}

public abstract class TupleBase<T1, T2>
{
    public T1 Argument1 { get; private set; }
    public T2 Argument2 { get; private set; }

    public TupleBase(T1 argument1, T2 argument2)
    {
       this.Argument1 = argument1;
       this.Argument2 = argument2;
    }
}

public abstract class TupleBase<T1, T2, T3>
{
    public T1 Argument1 { get; private set; }
    public T2 Argument2 { get; private set; }
    public T3 Argument3 { get; private set; }

    public TupleBase(T1 argument1, T2 argument2, T3 argument3)
    {
       this.Argument1 = argument1;
       this.Argument2 = argument2;
       this.Argument3 = argument3;
    }
}

//  Etc..

Тогда

class MyClass
{
    int var1;
    int var2;
    int var3;
    int var4;

    public MyClass(int var1, int var2, int var3, int var4){
        this.var1 = var1;
        this.var2 = var2;
        this.var3 = var3;
        this.var4 = var4;
    }
}

становится

class MyClass : TupleBase<int, int, int, int>
{
    public MyClass(int var1, int var2, int var3, int var4)
        :   base(var1, var2, var3, var4)
    {
    }
}

.

Наследование является фундаментально правильным инструментом для этого, поскольку вы хотите MyClass подобрать по базовому шаблону, т.е. наследовать его.Проблема, с которой вы столкнетесь в C #, заключается в том, что вы можете наследовать только от одного класса за раз, поэтому вы не можете просто использовать дополнительные функции, подобные этой, во всех случаях.

В качестве альтернативы вы можете написать

class MyClass
{
    int var1;
    int var2;
    int var3;
    int var4;

    public MyClass(int var1, int var2, int var3, int var4){
        this.SetConstructorValues(var1, var2, var3, var4);
    }
}

, где .SetConstructorValues() - это общий метод расширения

private static System.Collections.ConcurrentDictionary<Type, Action<object, object[]>> ConstructorSetterDictionary { get; private set; }

public static void SetConstructorValues<T_SetClass>(
            this T_SetClass instanceToSetValuesFor
        ,   params object[] constructorArguments
    )
{
    var instanceType = typeof(T_SetClass);

    Action<object, object[]> constructorSetterAction;
    if (!ConstructorSetterDictionary.TryGetValue(
                instanceType
            ,   out constructorSetterAction
        ))
    {
        throw new Exception("Populate the dictionary!  Also change this Exception message; it's from a StackOverflow example and not really designed to actually be written like this.");
    }

    constructorSetterAction(
                instanceToSetValuesFor
            ,   constructorArguments
        );
}

, где ConstructorSetterDictionary - словарь установщика - Action<object, object[]> для каждого Typeвыводится в соответствии с любой логикой, которая вам нравится (например, сопоставление конструкторов с именами параметров для полей), заполняется при запуске программы с использованием отражения.

Концептуально, получение метода, подобного этому, основанного на Type объекта, в основном заключается в том, какработают виртуальные методы, где ConstructorSetterDictionary - это виртуальная таблица поиска.

Я считаю, что такого рода метапрограммирование полезно в моей собственной работе, но предупреждаю, что в какой-то момент он перестает быть C #.

0 голосов
/ 22 февраля 2019

Short: Нет, Long: Да, есть хак.

Вы можете использовать сочетание отражения и сохранения параметра во временном массиве для достижения этого.

class TestClass
{
    public string var1 { get; set; }
    public string var2 { get; set; }
    public string var3 { get; set; }

    public TestClass(string var1, string var2, string var3) : base()
    {
        var param = new { var1, var2, var3 };
        PropertyInfo[] info = this.GetType().GetProperties();

        foreach (PropertyInfo infos in info) {
            foreach (PropertyInfo paramInfo in param.GetType().GetProperties()) {
                if (infos.Name == paramInfo.Name) {
                    infos.SetValue(this, paramInfo.GetValue(param, null));
                }
            }
        }

    }

}

Это в основном перебирает свойства и проверяет, совпадает ли имя с именем параметра, которое было сохранено во временном массиве (вы не можете получить значение параметра с отражением), и присваивает его, если они совпадают.

Примечание: я не рекомендую присваивать такие свойства, но ради доказательства того, что это возможно, я придумал это.

0 голосов
/ 22 февраля 2019

Проще говоря, вы не можете.Использование этого не обязательно, но это элегантно и демонстрирует намерение подчеркнуть тот факт, что переменная является частью контекста класса.

Однако проблема здесь возникает из-за того, что ваши параметры и переменные экземпляра имеют одинаковые имена.

Компилятор не может различить переменные с одинаковыми именами (это ночные жалобы на циклическую ссылку),

Использование ключевого слова this позволяет нам сообщить компилятору, что мы ссылаемся на текущий экземпляр этой переменной.

Я думаю, что вы можете улучшить код и кодирование как таковое с помощьюлучший подход к именам для ваших переменных.

Например, var1 var2 var3 они на самом деле ничего не говорят и затрудняют понимание кода.

Попытайтесь быть конкретными и подробными: например, firstName, lastName, адрес и пр.Они говорят сами за себя.

0 голосов
/ 22 февраля 2019

Нет, в текущей версии C # нет способа сделать это проще.Для решения этой проблемы в предварительных выпусках C # 6.0 появилась новая функция под названием «Основные конструкторы», но она была удалена до окончательного выпуска.https://www.c -sharpcorner.com / UploadFile / 7ca517 / primary-constructor-is-удал-from-C-Sharp-6-0 /

В настоящее время я считаю, что команда C #добавление записей в язык: https://github.com/dotnet/roslyn/blob/features/records/docs/features/records.md - это должно упростить работу с простыми классами данных, как в F #

0 голосов
/ 22 февраля 2019

Если вы сначала определили свои переменные, вы можете использовать инструмент «Быстрые действия» для Visual Studio, чтобы сгенерировать конструктор для вас;это дает вам возможность выбора определенных в настоящее время полей класса для включения.

, с помощью которого будет вставлен класс конструктора со всеми выбранными вами полями в качестве параметров, и будут назначены значения для полей.

Это не уменьшит объем кода, но сократит количество набираемого текста

0 голосов
/ 22 февраля 2019

Ответ на ваш вопрос - нет, но вы можете использовать свойства, чтобы получить аналогичное исправление:

class MyClass
{
    public int Var1 {get;set;}
    public int Var2 {get;set;}
    public int Var3 {get;set;}
    public int Var4 {get;set;}
    public MyClass(){

    }
}

void Main()
{
   var myClass = new MyClass
   {
     Var1 = 1,
     Var2 = 2,
     Var3 = 3,
   };
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...