是否可以将属性作为参数传递

时间:2011-12-19 23:18:48

标签: c#

如果这听起来太不可能了,请不要投票:)只是我的怪癖,我想知道我是否可以将属性作为参数传递给函数以获得一些结果。

例如。

void delete_button_click ()
{
    foreach (DataGridViewRow row in dgvRecords.SelectedRows)
        dgvRecords.Rows.Remove(row);
}

并假设

void delete_all_button click ()
{
    foreach (DataGridViewRow row in dgvRecords.Rows)
        dgvRecords.Rows.Remove(row);
}

我可以在一个函数中编写上述两个代码,因为唯一的区别是SelectedRowsRows。像这样:

void button_click (/*DataGridView property rows*/)
{
    foreach (DataGridViewRow row in dgvRecords.rows)
        dgvRecords.Rows.Remove(row);
}

并称之为

{
   button_click(DataGridView.Rows);
   //and
   button_click(DataGridView.SelectedRows);
}

另一个例如,

decimal GetAmount1(User usr)
{
    decimal sum = 0;
    foreach (DataGridViewRow row in dgvRecords.Rows)
    {
        Tuple<User, User, Notification, Acknowledgment> tup = (Tuple<User, User, Notification, Acknowledgment>)row.Tag;
        if (tup.Item1 == usr)
            sum += tup.Item4.Sum;

    }
    return sum;
}

decimal GetAmount2(User usr)
{
    decimal sum = 0;
    foreach (DataGridViewRow row in dgvRecords.Rows)
    {
        Tuple<User, User, Notification, Acknowledgment> tup = (Tuple<User, User, Notification, Acknowledgment>)row.Tag;
        if (tup.Item2 == usr)
            sum += tup.Item4.Sum;

    }
    return sum;
}

上述代码的唯一区别只是属性。这是可用的:

decimal GetAmount(User usr, Tuple<User, User, Notification, Acknowledgment>.Property Item)
{
    decimal sum = 0;
    foreach (DataGridViewRow row in dgvRecords.Rows)
    {
        Tuple<User, User, Notification, Acknowledgment> tup = (Tuple<User, User, Notification, Acknowledgment>)row.Tag;
        if (tup.Item == usr)
            sum += tup.Item4.Sum;

    }
    return sum;
}

并致电:

{ 
     GetAmount(usr, Tuple<User, User, Notification, Acknowledgment>.Item1)
     //and
     GetAmount(usr, Tuple<User, User, Notification, Acknowledgment>.Item2)   
}

...

4 个答案:

答案 0 :(得分:3)

您的第一个示例可以通过使用普通参数来解决:

删除行的方法的签名是:

void RemoveRows (IList rows)
{
    foreach (var row in rows)
        dgvRecords.Rows.Remove(row);
}

并将其命名为:

RemoveRows (DataGridView.Rows);
//or
RemoveRows (DataGridView.SelectedRows);

第二个示例需要使用Func<T, TResult>,它允许您定义要选择的属性。

所以你的GetAmount()方法siganture变为:

decimal GetAmount(User usr, Func<Tuple<User, User, Notification, Acknowledgment>, User> selector)
{
    decimal sum = 0;
    foreach (DataGridViewRow row in dgvRecords.Rows)
    {
        Tuple<User, User, Notification, Acknowledgment> tup = (Tuple<User, User, Notification, Acknowledgment>)row.Tag;
        if (selector(tup) == usr)
            sum += tup.Item4.Sum;

    }
    return sum;
}

你会称之为:

GetAmount(usr, tuple => tuple.Item1);
//or
GetAmount(usr,  tuple => tuple.Item1);

答案 1 :(得分:1)

你真的不需要投影来解决第一个问题,但是为了想要选择一个属性的参数,你可以传入一个投影以允许调用者选择属性,例如在你的第一个你可以做的一套方法。

   void DeleteRows(DataGridView dgv, Func<DataGridView, IEnumerable> projection)
   {
        foreach (DataGridViewRow row in projection(dgv))
            dgv.Rows.Remove(row);
   }

或者,使用泛型:

   void DeleteRows<TCollection>(DataGridView dgv, Func<DataGridView, TCollection> projection) where TCollection : IEnumerable
   {
        foreach (DataGridViewRow row in projection(dgv))
            dgv.Rows.Remove(row);
   }

然后你可以这样称呼它:

   // delete all rows
   DeleteRows(dgvRecords, d => d.Rows);

   // delete only selected rows
   DeleteRows(dgvRecords, d => d.SelectedRows);

也就是说,在任何你想要动态传递属性的情况下,而是传入一个接收对象的Func<...>并返回你想要的属性。这与LINQ通过允许调用者指定查询数据的投影来创建此类通用算法非常相似。

当然,如果您有public个方法接受代理,请在调用它们之前确保它们不是null。如果它们是您控制的private种方法,那么如果您控制所有对它们的调用,则不会出现大问题。

更新例如2:

decimal GetAmount(User usr, Func<Tuple<User, User, Notification, Acknowledgement>, User> projection)
{
    decimal sum = 0;
    foreach (DataGridViewRow row in dgvRecords.Rows)
    {
        Tuple<User, User, Notification, Acknowledgment> tup = (Tuple<User, User, Notification, Acknowledgment>)row.Tag;
        if (projection(tup) == usr)
            sum += tup.Item4.Sum;

    }
    return sum;
}

并致电:

GetAmount(user, t => t.Item1);

或者

GetAmount(user, t => t.Item2);

由于大Tuple,但是仍然有效,这个有点丑陋。

答案 2 :(得分:1)

你可以用反射做到这一点,但我不推荐它。以下是获取属性“getter”并使用它的示例:

    int someFunction(Element instance, string propertyName)
    {
        MethodInfo prop = typeof(Element).GetProperty(propertyName).GetGetMethod();
        if (prop.ReturnType != typeof(int))
        {
            throw new Exception("Type of property does not match expected type of int");
        }
        return (int)prop.Invoke(instance, null);
    }

同样,除非你有比上面提到的更好的理由,否则我不建议做这样的事情。如果你做这样的事情,你应该努力缓存MethodInfo对象,这样你就不需要每次都重新创建它。

答案 3 :(得分:0)

这是@Alastair Pitts方法,但我稍微更改了代码以使其正常工作。

 void RemoveRows (IEnumerable rows)
 {
     foreach (DataGridViewRow row in rows)
         dgvRecords.Rows.Remove(row);
 }

并称之为

RemoveRows ((IEnumerable)dgvRecords.Rows);
//or
RemoveRows ((IEnumerable)dgvRecords.SelectedRows);

我曾尝试使用IEnumerable&lt;&gt;正如阿拉斯泰尔所指出的那样,它无济于事。将非泛型转换为泛型是不可能的。 IEnumerable位于System.Collections名称空间中,而不是像{IE1}}名称空间中那样的IEnumerable&lt;&gt;。尽管如此,我还是将其作为答案。