Question

What I am actually trying to do is write a function that allows me to change the selection in a DataGridView and I would like to write one function and use it for both rows and columns. Here is a simple example that deselects everything and selects a new row or column:

private void SelectNew<T>(T collection, int index) where T : IList
{
  ClearSelection();
  collection[index].Selected = true;
}

My problem is that this does not work because it cannot be derived that .Selected() is available because this is the non-generic IList.

Using

where T : IList<DataGridViewBand>

would be nice but since DataGridViewRowCollection (and -Column-) just derive from IList this doesn't work.

In C++ I would probably use the traits idiom. Is there a way to do that in C# or is there a more idiomatic way?

Was it helpful?

Solution

While it's theoretically possible to use reflection to do this; since your explicit goal is just to handle rows or columns the easiest option is to just create two overloads for the function:

private void SelectNew(DataGridViewColumnCollection collection, int index)
{
    ClearSelection();
    collection[index].Selected = true;
}

private void SelectNew(DataGridViewRowCollection collection, int index)
{
    ClearSelection();
    collection[index].Selected = true;
}

If you tried to use reflection to do this it would work, but it would be slower, less readable, and has the danger of no compile time protection; people would be able to pass in other kinds of lists that didn't have a Selected property and it would compile and just fail at runtime.

OTHER TIPS

One possibility would be to use dynamic:

private void SelectNew(IList collection, int index)
{
  ClearSelection();
  ((dynamic)collection)[index].Selected = true;
}

Or:

private void SelectNew(IList collection, int index)
{
  ClearSelection();
  DataGridViewBand toSelect = ((dynamic)collection)[index];
  toSelect.Selected = true;
}

The biggest disadvantage of this is that you're losing compile-time type safety, so I wouldn't recommend this unless it prevented a significant amount of code duplication.

(The second version has a bit more compile-time type safety, at the cost of being more verbose and explicit.)

If you have a collection that implements IEnumerable, and you know ahead of time what type of elements it contains, you can do the following:

IList<DataGridViewBand> typedCollection = collection
                                          .Cast<DataGridViewBand>()
                                          .ToList();

which would allow you to call your generic extension method:

private void SelectNew<T>(T collection, int index)
   where T : IList<DataGridViewBand>
{
  ClearSelection();
  collection[index].Selected = true;
}

typedCollection.SelectNew(1);

Edit:

If you decide you want to constrain T on IList<DataGridViewBand>, you may as well just write a method directly for that type because you gain nothing by using generics.

IList<DataGridViewBand> typedCollection = collection
                                          .Cast<DataGridViewBand>()
                                          .ToList();

private void SelectNew(IList<DataGridViewBand> collection, int index)
{
  ClearSelection();
  collection[index].Selected = true;
}

typedCollection.SelectNew(1);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top