如何在C#中通过引用传递属性

本文关键字:引用 属性 | 更新日期: 2023-09-27 18:21:51

我有一个进程,我想将它应用于任意对象的多个值类型属性,这样每个属性都会被进程以某种方式修改。将过程应用于传递给它的任何给定属性的方法似乎是可行的,但由于属性是一种值类型,除非我通过引用传递它,否则它不会被更改,但当然,C#编译器会阻止通过引用传递属性。

在编译器不反对或不必为每个属性重复相同条件代码的情况下,我如何实现以下目标?

        static internal void AssignStringValueOrLeaveIfNull(string newValue, string sampleValue)
        {
            if (!string.IsNullOrEmpty(newValue))
                sampleValue = newValue;
        }
...
            AssignStringValueOrLeaveIfNull(value1, anObject.SampleText1);
            AssignStringValueOrLeaveIfNull(value2, anObject.SampleText2);
            AssignStringValueOrLeaveIfNull(value3, anObject.SampleText3);
            AssignStringValueOrLeaveIfNull(value4, anObject.SampleText4);
            AssignStringValueOrLeaveIfNull(value5, anObject.SampleText5);
...etc, 30 times.

其中,Object.SampleTextn都是字符串。

我不可能是第一个想做类似事情的人!

我使用的是VS2008(C#3.5)

TIA

如何在C#中通过引用传递属性

您不能。这一概念并不存在。您必须将值分配给一个临时本地变量,在变量上使用ref,然后将其分配回属性:

var tmp = anObject.SampleText1;
AssignStringValueOrLeaveIfNull(value1, ref tmp);
anObject.SampleText1 = tmp;

或者使用返回值,这可能更简单。。。

anObject.SampleText1 = AssignStringValueOrLeaveIfNull(value1, anObject.SampleText1);

ref与一起工作

  • 字段
  • 局部变量
  • 数组元素
  • 参数

它不能处理属性,因为属性实际上是方法调用,并且方法调用的结果对于ref没有一个合理的位置。注意:在IL级别,可以从方法中获得ref返回值,这在理论上允许类似的操作,但它目前没有在C#中公开(如果有的话),而且它不能与当前存在的属性一起使用。

您可以编写一个丑陋的扩展方法,该方法使用一个代表您要设置的属性的表达式,并在分配值之前让它有机会检查您的新值是null还是空的(或与目标不同)。

public static void SetPropertyValue<T>(this T target, Expression<Func<T, string>> memberLamda, string value)
{
    // Check if "new value" is null or empty and bail if so
    if (string.IsNullOrEmpty(value))
        return;
    var memberSelectorExpression = memberLamda.Body as MemberExpression;
    if (memberSelectorExpression != null)
    {
        var property = memberSelectorExpression.Member as PropertyInfo;
        if (property != null)
        {
            // Get the existing value and compare against the new value 
            // Only set the property if it's different from the existing value
            if ((string)property.GetValue(target, null) != value)
            {
                property.SetValue(target, value, null);
            }
        }
    }
}

来源

然后你可以像这样使用它:

anObject.SetPropertyValue(a => a.SampleText1, value1);
anObject.SetPropertyValue(a => a.SampleText2, value2);

这应该可以避免将对象标记为"脏",但成本相当高(正如Marc在回答中提到的那样)。