The Problem
It's something I came across a while back and was able to work around it somehow. But now it came back, feeding on my curiosity - and I'd love to have a definite answer.
Basically, I have a generic dgv BaseGridView<T> : DataGridView where T : class
. Constructed types based on the BaseGridView
(such as InvoiceGridView : BaseGridView<Invoice>
) are later used in the application to display different business objects using the shared functionality provided by BaseGridView
(like virtual mode, buttons, etc.).
It now became necessary to create a user control that references those constructed types to control some of the shared functionality (eg. filtering) from BaseGridView
. I was therefore hoping to create a public property on the user control that would enable me to attach it to any BaseGridView
in Designer/code: public BaseGridView<T> MyGridView { get; set; }
. The trouble is, it doesn't work :-) When compiled, I get the following message:
The type or namespace name 'T' could not be found (are you missing a using directive or an assembly reference?)
Solutions?
I realise I could extract the shared functionality to an interface, mark BaseGridView
as implementing that interface, and then refer to the created interface in my uesr control.
But I'm curious if there exists some arcane C# command/syntax that would help me achieve what I want - without polluting my solution with an interface I don't really need :-)
EDIT: For reference, I did try this innocent workaround: BaseGridView<object> MyGridView { get; set; }
, and... it still isn't the answer: Cannot implicitly convert type 'InvoiceGridView' to 'BaseGridView<object>'.
Partial success (edit 2)
Ok, because covariance is only supported on interfaces, I admitted defeat and defined an interface (only showing some of it):
public interface IBaseGridView<out T> where T : class
{
bool ScrollTo(Predicate<T> criteria);
bool ScrollTo(T object);
}
I am now able to cast my beloved InvoiceGridView
to an IBaseGridView<object>
- which is awesome and I'm a happy boy again :-) However, the second ScrollTo
is giving me trouble upon compilation:
Invalid variance: The type parameter 'T' must be contravariantly valid on 'GeParts.Controls.IBaseGridView.ScrollTo(T)'. 'T' is covariant.
I'm now having to modify the signature to ScrollTo(object o)
- which isn't ideal but gets the job done. What suprised me was that the compiler complained about the second ScrollTo
yet was happy with the first one. So it seems that one isn't allowed to pass instances of an out T
, but using the type itself (eg. in Predicate<T>
) is fine? Seems rather picky...