【问题标题】:Pass property itself to function as parameter in C#将属性本身传递给 C# 中的参数
【发布时间】:2012-06-26 02:10:14
【问题描述】:

我正在寻找一种将属性本身传递给函数的方法。不是财产价值。函数事先并不知道将使用哪个属性进行排序。此示例中最简单的方法是:使用不同的参数类型创建 4 个覆盖。其他方法是使用 typeof() 内部函数。当 Class1 有数百个属性时,这两种方式都是不可接受的。到目前为止,我找到了以下方法:

class Class1
{
    string vehName;
    int maxSpeed;
    int fuelCapacity;
    bool isFlying;
}

class Processor
{
    List<Class1> vehicles = null;
    Processor(List<Class1> input)
    {
        vehicles = input;
    }

    List<Class1> sortBy(List<Class1> toSort, string propName)
    {
        if (toSort != null && toSort.Count > 0)
        {
            return toSort.OrderBy(x => typeof(Class1).GetProperty(propName).GetValue(x, null)).ToList();
        }
        else return null;
    }
}

class OuterUser
{
    List<Class1> vehicles = new List<Class1>();
    // ... fill the list
    Processor pr = new Processor(vehicles);
    List<Class1> sorted = pr.sortBy("maxSpeed");
}

我不喜欢这种方法,因为在将字符串传递给处理函数时存在“人为错误”的风险。当字符串由代码的其他部分生成时,这将更加难看。 请提出更优雅的方法来实现 Class1 属性的传递以进行进一步处理。使用恕我直言的最佳选择将是(或类似的东西):

vehicles = sortBy(vehicles, Class1.maxSpeed);

【问题讨论】:

标签: c# properties parameter-passing


【解决方案1】:

你为什么不使用 Linq 呢?喜欢:

vehicles.OrderBy(v => v.maxSpeed).ToList();

【讨论】:

  • 其他类将使用Processor。他们不应该看到vehicles。另外sortBy会有更多的逻辑(升序/降序/一些过滤)...
  • 那我觉得你应该看看上面@olivier 的回答,关于编写自己的扩展方法(添加过滤等)
【解决方案2】:

您可以将属性访问器传递给方法。

List<Class1> SortBy(List<Class1> toSort, Func<Class1, IComparable> getProp)
{
    if (toSort != null && toSort.Count > 0) {
        return toSort
            .OrderBy(x => getProp(x))
            .ToList();
    }
    return null;
}

你可以这样称呼它:

var result = SortBy(toSort, x => x.maxSpeed);

但你可以更进一步,编写自己的扩展方法。

public static class CollectionExtensions
{
    public static List<TSource> OrderByAsListOrNull<TSource, TKey>(
        this ICollection<TSource> collection, Func<TSource,TKey> keySelector)

        if (collection != null && collection.Count > 0) {
            return collection
                .OrderBy(x => keySelector(x))
                .ToList();
        }
        return null;
    }
}

现在你可以这样排序

List<Class1> sorted = toSort.OrderByAsListOrNull(x => x.maxSpeed);

还有

Person[] people = ...;
List<Person> sortedPeople = people.OrderByAsListOrNull(p => p.LastName);

请注意,我将第一个参数声明为ICollection&lt;T&gt;,因为它必须满足两个条件:

  1. 它必须有一个Count 属性
  2. 它必须是IEnumerable&lt;T&gt; 才能应用LINQ 方法OrderBy

List&lt;Class1&gt; 是一个 ICollection&lt;T&gt;,但也是一个数组 Person[] 和许多其他集合一样。


到目前为止,我已经展示了如何读取属性。如果方法需要设置一个属性,你也需要传递一个 setter 委托

void ReadAndWriteProperty(Func<Class1, T> getProp, Action<Class1, T> setProp)

其中T 是属性的类型。

【讨论】:

  • 这个方法和 LINQ 的 OrderBy 有什么不同吗?
  • @Chris Gessler:是的,如果源集合是null 或其Count0,则返回null,否则返回List&lt;T&gt;。如果源是null,LINQ 的OrderBy 将引发异常,否则返回IEnumerable&lt;T&gt;。我不知道这种方法对其他人是否真的有用,但 OP 显然需要它。
【解决方案3】:

您可以使用 lambda 表达式来传递属性信息:

void DoSomething<T>(Expression<Func<T>> property)
{
    var propertyInfo = ((MemberExpression)property.Body).Member as PropertyInfo;
    if (propertyInfo == null)
    {
        throw new ArgumentException("The lambda expression 'property' should point to a valid Property");
    }
}

用法:

DoSomething(() => this.MyProperty);

【讨论】:

  • 如果您需要获取有关属性的更多信息,例如属性名称(在实现INotifyPropertyChanged 时很有用),您可以使用此方法。但是,在这种情况下这样做有点过头了,因为返回属性值就足够了。
【解决方案4】:

只是从上面的答案中添加。您也可以为订单方向做一个简单的标志。

public class Processor
{
    public List<SortableItem> SortableItems { get; set; }

    public Processor()
    {
        SortableItems = new List<SortableItem>();
        SortableItems.Add(new SortableItem { PropA = "b" });
        SortableItems.Add(new SortableItem { PropA = "a" });
        SortableItems.Add(new SortableItem { PropA = "c" });
    }

    public void SortItems(Func<SortableItem, IComparable> keySelector, bool isAscending)
    {
        if(isAscending)
            SortableItems = SortableItems.OrderBy(keySelector).ToList();
        else
            SortableItems = SortableItems.OrderByDescending(keySelector).ToList();
    }
}

【讨论】:

    【解决方案5】:

    我发现@MatthiasG 的回答中缺少的是如何获取属性值而不仅仅是其名称。

    public static string Meth<T>(Expression<Func<T>> expression)
    {
        var name = ((MemberExpression)expression.Body).Member.Name;
        var value = expression.Compile()();
        return string.Format("{0} - {1}", name, value);
    }
    

    使用:

    Meth(() => YourObject.Property);
    

    【讨论】:

      【解决方案6】:

      这里的解决方案很好......

      Passing properties by reference in C#

      void GetString<T>(string input, T target, Expression<Func<T, string>> outExpr)
      {
          if (!string.IsNullOrEmpty(input))
          {
              var expr = (MemberExpression) outExpr.Body;
              var prop = (PropertyInfo) expr.Member;
              prop.SetValue(target, input, null);
          }
      }
      
      void Main()
      {
          var person = new Person();
          GetString("test", person, x => x.Name);
          Debug.Assert(person.Name == "test");
      }
      

      【讨论】:

        【解决方案7】:

        我想给出一个简单易懂的答案。

        函数的参数是这样的:System.Func&lt;class, type of the property&gt;

        我们像这样传递属性:Function(x =&gt; x.Property);

        代码如下:

        class HDNData
        {
            private int m_myInt;
            
            public int MyInt
            {
                get { return m_myInt; }
            }
            
            public void ChangeHDNData()
            {
                if (m_myInt == 0)
                    m_myInt = 69;
                else
                    m_myInt = 0;
            }
        }
        
        static class HDNTest
        {
            private static HDNData m_data = new HDNData();
            
            public static void ChangeHDNData()
            {
                m_data.ChangeHDNData();
            }
            
            public static void HDNPrint(System.Func<HDNData, int> dataProperty)
            {
                Console.WriteLine(dataProperty(m_data));//Print to console the dataProperty (type int) of m_data
            }
        }
        
        //******Usage******
        HDNTest.ChangeHDNData();
        //This is what you want: Pass property itself (which is MyInt) to function as parameter in C#
        HDNTest.HDNPrint(x => x.MyInt);
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-02-05
          • 2015-10-26
          • 1970-01-01
          • 1970-01-01
          • 2018-08-03
          相关资源
          最近更新 更多