Reputation: 73163
If this sounds too impossible, pls dont down vote :) Just my quirk that I wonder if ever I can pass a property as the parameter to a function to get some result.
For eg.
void delete_button_click ()
{
foreach (DataGridViewRow row in dgvRecords.SelectedRows)
dgvRecords.Rows.Remove(row);
}
And suppose
void delete_all_button click ()
{
foreach (DataGridViewRow row in dgvRecords.Rows)
dgvRecords.Rows.Remove(row);
}
Can I write the above two codes in one function since the only difference is SelectedRows
and Rows
. Something like this:
void button_click (/*DataGridView property rows*/)
{
foreach (DataGridViewRow row in dgvRecords.rows)
dgvRecords.Rows.Remove(row);
}
And call it
{
button_click(DataGridView.Rows);
//and
button_click(DataGridView.SelectedRows);
}
Another eg.,
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;
}
and
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;
}
The only difference in above codes are just a property. So is this available:
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;
}
And call :
{
GetAmount(usr, Tuple<User, User, Notification, Acknowledgment>.Item1)
//and
GetAmount(usr, Tuple<User, User, Notification, Acknowledgment>.Item2)
}
??
Upvotes: 2
Views: 274
Reputation: 38397
You don't really need a projection to solve the first problem, but for the sake of argument of wanting to select a property, you can pass in a projection to allow the caller to select the property, for example in your first set of methods, you could do.
void DeleteRows(DataGridView dgv, Func<DataGridView, IEnumerable> projection)
{
foreach (DataGridViewRow row in projection(dgv))
dgv.Rows.Remove(row);
}
Or, alternatively using generics:
void DeleteRows<TCollection>(DataGridView dgv, Func<DataGridView, TCollection> projection) where TCollection : IEnumerable
{
foreach (DataGridViewRow row in projection(dgv))
dgv.Rows.Remove(row);
}
Then you can call it like:
// delete all rows
DeleteRows(dgvRecords, d => d.Rows);
// delete only selected rows
DeleteRows(dgvRecords, d => d.SelectedRows);
That is, in any case where you want to pass in a property dynamically, instead pass in a Func<...>
that takes in the object and returns the property you want. This is very similar how LINQ creates such generic algorithms by allowing the caller to specify the projections on which to query the data.
Of course, if you have public
methods taking in delegates, make sure they are non-null
before invoking them. If they're private
methods under your control, it's not as big of an issue if you control all the calls to them.
UPDATE For example 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;
}
And call:
GetAmount(user, t => t.Item1);
Or
GetAmount(user, t => t.Item2);
That one's a bit uglier because of the large Tuple
but still works.
Upvotes: 1
Reputation: 73163
This is @Alastair Pitts method, but I change the code a bit to get it working.
void RemoveRows (IEnumerable rows)
{
foreach (DataGridViewRow row in rows)
dgvRecords.Rows.Remove(row);
}
And call it
RemoveRows ((IEnumerable)dgvRecords.Rows);
//or
RemoveRows ((IEnumerable)dgvRecords.SelectedRows);
I had tried with IEnumerable<> as pointed out by Alastair but it wouldnt work. It's not possible to cast a non-generic to a generic. IEnumerable is in the System.Collections
namespace and not in the System.Collections.Generic
namespace like IEnumerable<>. Nevertheless, I'm marking his as the answer.
Upvotes: 0
Reputation: 19591
Your first example is can be solved by just using a normal parameter:
The signature of a method to remove rows would be:
void RemoveRows (IList rows)
{
foreach (var row in rows)
dgvRecords.Rows.Remove(row);
}
and call it with:
RemoveRows (DataGridView.Rows);
//or
RemoveRows (DataGridView.SelectedRows);
The second example requires the use of a Func<T, TResult>
which allows you to define what property you want to select.
So your GetAmount()
method siganture becomes:
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;
}
and you would call it:
GetAmount(usr, tuple => tuple.Item1);
//or
GetAmount(usr, tuple => tuple.Item1);
Upvotes: 3
Reputation: 2543
You can do this with reflection, but I wouldn't recommend it. Here is an example of getting a properties "getter" and working with it:
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);
}
Again, I would not recommend doing anything like this unless you had a better reason than mentioned above. If you do something like this, you should try hard to cache the MethodInfo object so you don't need to recreate it every time.
Upvotes: 1