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

本文关键字:array 类似 merge PHP 两个 方式 对象 合并 | 更新日期: 2023-09-27 18:14:33

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中一样简单的方式来回答。

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

我找不到一个完全符合我要求的答案。我写了一个小方法来做这个。它将同时使用两个对象,并合并它们的字段/属性,假设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);
    }
}