当我使用 Action<> 设置对象时,分配的对象始终为 null

When I set an object using an Action<> the object assigned is always null

我的目标是创建一个 Action<> 用于在构造函数中设置配置对象。

我定义的Action<>,通过引用设置了两个配置对象,但问题是分配的对象总是空的。我认为在内部,对象是通过引用分配的,但似乎没有。

在示例代码中,我在主程序中创建了一个 CarConfiguration,我尝试使用 Action<> 将此配置引用到我的新 Car 中,该操作定义了主程序 CarConfiguration 和 Car 属性之间的分配配置。

即使在我的 Action<> 方法中通过引用分配,为什么我的汽车配置属性始终为 null?

主要Class:

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());

配置Class:

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);
}

汽车Class:

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();
    }
}

如果您使用 Action<ref CarConfiguration>(假设这是合法的)而不是 Action<CarConfiguration> 来允许通过引用传递参数,您的代码将起作用。但是没有build-inAction<ref Τ>,想要的还是自己做吧:

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

...然后使用 RefAction<CarConfiguration> 而不是 Action<CarConfiguration>

您的操作是将配置分配给 lambda 参数,这没有任何效果,因为参数是输入值,它不会从 lambda 中返回。对象通过引用传递,但引用被复制到函数调用中的参数,即当您像这样调用操作时 conf 将收到对 carConfiguration 的引用的副本 configureCar(carConfiguration);

分配和覆盖此引用的本地副本没有任何用处。您必须使用 ref 关键字实质上将对引用(变量)的引用传递给对象。当分配给标有 ref 的变量时,它将覆盖原始成员变量中保存的引用,而不是 lambda 中的局部变量。这已经在另一个答案中得到证明。

实现您想要完成的目标的正确方法不是通过传递引用,而是通过在操作中配置对象。如果您想使用现有配置,只需像您已经这样做的那样传递对象。无需编写以显式方式接受对象引用的操作。

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);

注意配置是如何实例化和存储的-第一次访问它,也许只有在构造函数中接受一个函数才有用。