【问题标题】:Modify fields in extraneous function修改无关函数中的字段
【发布时间】:2023-02-05 23:58:36
【问题描述】:

我有很多重复的代码地方:

if (claimSettingHistoryDto.NewClaimTypeName == claimSettingHistoryDto.OldClaimTypeName)
                {
                    claimSettingHistoryDto.NewClaimTypeName = null;
                    claimSettingHistoryDto.OldClaimTypeName = null;
                }

                if (claimSettingHistoryDto.NewApplicantName == claimSettingHistoryDto.OldApplicantName)
                {
                    claimSettingHistoryDto.NewApplicantName = null;
                    claimSettingHistoryDto.OldApplicantName = null;
                }

                if (claimSettingHistoryDto.NewDamageSparePartsTotalCostInsertion == claimSettingHistoryDto.OldDamageSparePartsTotalCostInsertion)
                {
                    claimSettingHistoryDto.NewDamageSparePartsTotalCostInsertion = null;
                    claimSettingHistoryDto.OldDamageSparePartsTotalCostInsertion = null;
                }

所以不断地针对不同领域的不同类别

我希望我有这样的功能:

private void SetNull(object newData, object oldData)
{
   if (newData == oldData)
   {
newData = null;
      oldData = null;
   }
}

但我当然知道这不是真的,因为我只更改了函数内部的局部值。如何更改类字段?

【问题讨论】:

    标签: c# function class


    【解决方案1】:

    有多种方法可以做到这一点,在“好主意”到“坏主意”的范围内有不同的立场。

    字段作为ref参数(好主意)

    (...) 这不是真的,因为我只更改函数内部的局部值

    你错了,因为 refout 参数允许你在非本地更改值。

    如果您有权访问实际字段,则可以将它们作为 ref 参数传递:

    public class Dto
    {
        private string? _old;
        private string? _new;
        
        public string? Old => _old;
        
        public string? New => _new;
        
        public void Foo() {
            SetNullIfEqual(ref _new, ref _old);
        }
        
        private static void SetNullIfEqual<T>(ref T? newData, ref T? oldData) where T: class
        {
           if (newData == oldData)
           {
              newData = null;
              oldData = null;
           }
        }
    }
    

    有关作为参考传递的更多信息here

    这不适用于属性,即使它们具有默认设置器。属性不是字段,它们是伪装的方法。如果您无法访问实际字段...

    作为代表的属性(我的想法)

    ...只有访问属性,您才需要像这样将它们作为委托传递:

    public class Dto
    {
        public string? Old { get; set; } 
        public string? New { get; set; }
    }
    
    public class Outside
    {
        public void Foo(Dto dto) {
            SetNullIfEqual(() => dto.New, () => dto.Old, v => dto.New = v, v => dto.Old = v);
        }
        private static void SetNullIfEqual<T>(
            Func<T?> getNew,
            Func<T?> getOld,
            Action<T?> setNew,
            Action<T?> setOld) where T: class
        {
            if (getNew() == getOld())
            {
                setNew(null);
                setOld(null);
            }
        }
    }
    

    虽然这很笨重,但您不得不质疑它实际上节省了多少空间。与第一个建议一样在字段上工作的实例方法效果更好。

    当你有反思时,一切看起来都像钉子(可能是个坏主意)

    您也可以通过反射来做到这一点,这将消除所有安全性,提供更差的性能,但绝对是最大的灵活性。

    using System.Reflection;
    
    public class Dto
    {
        public string? Old { get; set; } 
        public string? New { get; set; }
    }
    
    public class Outside
    {
        public void Foo(Dto dto) {
            SetNullIfEqual(nameof(dto.New), nameof(dto.Old), dto);
        }
        
        private static void SetNullIfEqual<T>(
            string newPropName,
            string oldPropName,
            T instance)
        {
            PropertyInfo newProp = typeof(T).GetProperty(newPropName);
            PropertyInfo oldProp = typeof(T).GetProperty(oldPropName);
            
            if (Equals(newProp.GetValue(instance), oldProp.GetValue(instance)))
            {
                newProp.SetValue(instance, null);
                oldProp.SetValue(instance, null);
            }
        }
    }
    

    为简洁起见,我删除了所有错误处理。

    推荐

    我会选择 fields-as-ref-parameters 方式。如果有问题的方法在类型之外,所以它无法访问字段(请永远不要使用public字段),我只是将它移到类型中。在您的情况下,它是一堆称为SetClaimTypeNameSetApplicantName 等的方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-04-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-12-27
      • 2018-02-02
      • 1970-01-01
      相关资源
      最近更新 更多