Когда я устанавливаю объект с помощью Action <>, назначенный объект всегда равен нулю - PullRequest
0 голосов
/ 02 июня 2019

Моя цель - создать Action <> для установки объекта конфигурации в конструкторе.

Действие <>, которое я определил, устанавливает два объекта конфигурации по ссылке, но проблема в том, что назначенный объект всегда равен нулю. Я думал, что внутренне объекты были назначены по ссылке, но, похоже, нет.

В примере кода я создал CarConfiguration в основной программе, и я пытаюсь установить эту конфигурацию на моей новой машине с помощью действия <>, которое определяет назначение между основной программой CarConfiguration и конфигурацией атрибута Car.

Почему мой атрибут конфигурации автомобиля всегда равен нулю, даже если он назначается ссылкой в ​​моем методе Action <>?

Основной класс:

CarConfiguration carConfiguration = new CarConfiguration()
{
    CarName = "Ferrari",
    CarModel = "LaFerrari",
    Spolier = true,
    BuildDate = new DateTime(2018, 01, 01)
};

//Thats not work because the "conf" parameter is never assign in the Car constructor
Car myOwnCar = new Car(conf => 
{
    conf = carConfiguration;
});
Console.WriteLine(myOwnCar.CarConfigurationText());

//That works, but is not my purpose do it like this !
Car myOtherCar = new Car(carConfiguration);
Console.WriteLine(myOtherCar.CarConfigurationText());

Класс конфигурации:

public class CarConfiguration
{
    public bool Spolier { get; set; } = false;
    public string CarName { get; set; } = String.Empty;
    public string CarModel { get; set; } = String.Empty;
    public DateTime BuildDate { get; set; } = default(DateTime);
}

Класс автомобиля:

public class Car
{
    private CarConfiguration carConfiguration = null;

    //Thats not work because carConfiguration is not assigned in the Action as a reference
    public Car(Action<CarConfiguration> configureCar)
    {
        configureCar(carConfiguration);
    }

    //Thats works!
    public Car(CarConfiguration configureCar)
    {
        carConfiguration = configureCar;
    }

    public string CarConfigurationText()
    {
        StringBuilder strBuilder = new StringBuilder();

        if (carConfiguration != null)
        {
            strBuilder.AppendLine(carConfiguration.CarModel);
            strBuilder.AppendLine(carConfiguration.CarName);
            strBuilder.AppendLine(carConfiguration.Spolier.ToString());
            strBuilder.AppendLine(carConfiguration.BuildDate.ToString("mm-DD-yyyy"));
        }
        else
        {
            strBuilder.AppendLine("Car is not configure!");
        }

        return strBuilder.ToString();
    }
}

Link output example

Ответы [ 2 ]

0 голосов
/ 02 июня 2019

Ваше действие назначает конфигурацию лямбда-параметра, который не имеет никакого эффекта, так как параметр является входным значением, он не возвращается из лямбда-выражения.Объекты передаются по ссылке, но ссылки копируются в параметры в вызовах функций, т. Е. conf получит копию ссылки на carConfiguration при вызове действия, подобного этому configureCar(carConfiguration);

Назначение и перезаписьэта локальная копия ссылки не имеет смысла.Вы должны использовать ключевое слово ref, чтобы по существу передать ссылку на ссылку (переменную) на объект.При назначении переменной, помеченной ref, она будет перезаписывать ссылку, хранящуюся в исходной переменной-члене, а не локальную переменную внутри лямбды.Это уже продемонстрировано в другом ответе.

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

public Car(Action<CarConfiguration> configureCar)
{
  carConfiguration = new CarConfiguration();
  configureCar(carConfiguration);
}

// This is the common configuration pattern seen in .NET
Car myOwnCar = new Car(conf => 
{
    conf.CarName = "Ferrari";
    conf.CarModel = "LaFerrari"
    /** etc **/
});

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

public static class CarConfigurationExtensions
{
  public static void CopyTo(CarConfiguration this source, CarConfiguration dest){
    dest.CarName = source.CarName;
    dest.CarModel = source.CarModel;
    // etc
  }
}


Car myOwnCar = new Car(conf => carConfiguration.CopyTo(conf));

Но ни при каких обстоятельствах не пишется действие, которое принимает ref к локальной переменной вещь.Другой альтернативой является использование Func<CarConfiguration>, например, так, может быть, если вы хотите выполнить отложенную инициализацию.

public Car(Func<CarConfiguration> configurator)
{
  _configurator = configurator;
}

private Func<CarConfiguration> _configurator;
private CarConfiguration _carConfiguration;

public CarConfiguration CarConfiguration => 
  _carConfiguration ?? (_carConfiguration = _configurator());

Car myOwnCar = new Car(() => carConfiguration);

Обратите внимание, как создается и сохраняется конфигурация - при первом обращении к ней, возможно, единственнойвремя принятия функции в конструкторе полезно.

0 голосов
/ 02 июня 2019

Ваш код работал бы, если бы вместо Action<CarConfiguration> вы использовали Action<ref CarConfiguration> (предполагая, что это допустимо), чтобы разрешить передачу аргумента по ссылке. Но нет встроенного Action<ref Τ>, поэтому, если вы хотите его, вы должны сделать его самостоятельно:

public delegate void RefAction<T>(ref T arg1);

... и затем используйте RefAction<CarConfiguration> вместо Action<CarConfiguration>.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...