以与PHP的array_merge类似的方式合并两个对象

时间:2013-05-19 17:29:00

标签: c# php arrays reflection

PHP中,通常的做法是将数组作为类的选项传递,然后将该数组与另一个包含默认值的数组合并。

像这样。

class MyObject
{
     private static $defaults = array('value'=>10);
     private $settings;

     public function Something(array $settings = array())
     {
          $this->settings = array_merge(static::defaults,$settings);
     }
}

您可以使用jQuery或其他引入merge函数的库在JavaScript中执行某些操作。这些脚本允许您获取两个Javascript对象并将它们合并在一起。允许您使用一个作为默认值,另一个用于覆盖这些默认值。

我发现这个模式非常有用,因为它允许你配置一大组默认值,但只分配你需要的设置。

有没有在C#中做这样的事情?

我可以编写一个使用反射在公共属性上执行此操作的函数,但我认为这样的事情必须已经完成。

编辑:

这个问题之前已经在堆栈上被问过,但没有提供与PHP和Javascript中提供的相同简单性的方式。

1 个答案:

答案 0 :(得分:0)

我无法找到完全符合我想要的答案。所以我写了一个小方法来做到这一点。它将同时采用两个对象,并合并其字段/属性,假设null值表示未分配的字段/属性。

这是用法示例。创建一个类来保存通信类的选项,让通信类具有默认值,然后使用用户设置初始化通信。

示例设置类。

public class ComSettings
{
    public int? Port;
    public string? Address;
    public bool? KeepAlive;
}

在构造函数中使用这些设置的示例类。

public class ComLibrary
{
    private static ComSettings _defaults = new ComSettings { Port = 80, Address = "localhost" };

    protected ComSettings settings;

    public ComLibrary(ComSettings pSettings)
    {
        this.settings = ObjectMerge<ComSettings>(_defaults, pSettings);
    }
}

这会让不同的类使用ComSettings,但每个类可能有不同的默认值。唯一的限制是字段/属性必须支持null分配。

以下是ObjectMerge的实现。

    /// <summary>
    /// Creates a new object that contains the properties of the two objects merged together.
    /// </summary>
    /// <typeparam name="T">The class type to merge.</typeparam>
    /// <param name="pDefaults">Instance of the defaults object.</param>
    /// <param name="pSettings">Instance of the settings object.</param>
    /// <returns>A new instance of T with the merged results.</returns>
    public static T ObjectMerge<T>(T pDefaults, T pSettings, bool pMergeFields = true, bool pMergeProperties = true) where T : class, new()
    {
        T target = new T();
        Type type = typeof(T);
        List<MemberInfo> infos = new List<MemberInfo>(type.GetMembers());

        foreach (MemberInfo info in infos)
        {
            // Copy values from either defaults or settings
            if (pMergeFields && info.MemberType == MemberTypes.Field)
            {
                FieldInfo field = (FieldInfo)info;
                if (field.IsPublic)
                {
                    object value = field.GetValue(pSettings);
                    value = (value == null) ? field.GetValue(pDefaults) : value;
                    field.SetValue(target, value);
                }
            }

            // Copy values from either defaults or settings
            if (pMergeProperties && info.MemberType == MemberTypes.Property)
            {
                PropertyInfo prop = (PropertyInfo)info;
                if (prop.CanWrite && prop.CanRead)
                {
                    object value = prop.GetValue(pSettings, null);
                    value = (value == null) ? prop.GetValue(pDefaults, null) : value;
                    prop.SetValue(target, value, null);
                }
            }
        }

        return target;
    }

这是一个简单的单元测试。

/// <summary>
///This is a test class for CoreUtilsTest and is intended
///to contain all CoreUtilsTest Unit Tests
///</summary>
[TestClass()]
public class CoreUtilsTest
{
    /// <summary>
    /// A class to perform testing on.
    /// </summary>
    public class MyClassA
    {
        public string Param1;
        public string Param2;
        public string Param3;
    }

    /// <summary>
    /// A class to perform testing on.
    /// </summary>
    public class MyClassB
    {
        private string _param1;

        public string Param1
        {
            get { return _param1; }
            set { _param1 = value; }
        }
        private string _param2;

        public string Param2
        {
            get { return _param2; }
            set { _param2 = value; }
        }
        private string _param3;

        public string Param3
        {
            get { return _param3; }
            set { _param3 = value; }
        }
    }

    /// <summary>
    ///A test for SetProperties
    ///</summary>
    [TestMethod()]
    public void Merging_Fields()
    {
        MyClassA defaults = new MyClassA { Param1 = "defaults" };
        MyClassA settings = new MyClassA { Param2 = "settings" };
        MyClassA results = CoreUtils.ObjectMerge<MyClassA>(defaults, settings);

        Assert.AreEqual("defaults", results.Param1);
        Assert.AreEqual("settings", results.Param2);
        Assert.AreEqual(null, results.Param3);
    }

    [TestMethod()]
    public void Merging_Properties()
    {
        MyClassB defaults = new MyClassB { Param1 = "defaults" };
        MyClassB settings = new MyClassB { Param2 = "settings" };
        MyClassB results = CoreUtils.ObjectMerge<MyClassB>(defaults, settings);

        Assert.AreEqual("defaults", results.Param1);
        Assert.AreEqual("settings", results.Param2);
        Assert.AreEqual(null, results.Param3);
    }

}