将属性本身传递给 C# 中的参数

Posted

技术标签:

【中文标题】将属性本身传递给 C# 中的参数【英文标题】:Pass property itself to function as parameter in 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);

【问题讨论】:

首先如何选择用于排序的属性? How can I pass a property of a class as a parameter of a method? 的可能重复项 【参考方案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;,因为它必须满足两个条件:

    它必须有一个Count 属性 它必须是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);

【讨论】:

以上是关于将属性本身传递给 C# 中的参数的主要内容,如果未能解决你的问题,请参考以下文章

将多个参数从 c# 传递给 python

如何将 C# 中的二维数组传递给 C++ DLL?

将参数化的 md-date-locale 属性传递给 Angular JS 中的 md-datepicker

将ajax请求中的多个参数传递给函数后面的C#代码

C#中的引用传递值传递。

将回调参数从 C# 传递到非托管 C++