Не построенные дженерики как свойства (например, список <T>) - PullRequest
1 голос
/ 18 июня 2010

Проблема

Это то, с чем я столкнулся некоторое время назад и смог как-то обойти это.Но теперь он вернулся, питаясь моим любопытством - и я бы хотел получить определенный ответ.

По сути, у меня есть универсальный dgv BaseGridView<T> : DataGridView where T : class.Построенные типы, основанные на BaseGridView (например, InvoiceGridView : BaseGridView<Invoice>), позже используются в приложении для отображения различных бизнес-объектов с использованием общей функциональности, предоставляемой BaseGridView (например, виртуальный режим, кнопки и т. Д.).

Теперь стало необходимо создать пользовательский элемент управления, который ссылается на эти сконструированные типы, чтобы управлять некоторыми из функций shared (например, фильтрация) из BaseGridView.Поэтому я надеялся создать в пользовательском элементе управления открытое свойство, которое позволило бы мне присоединить его к любому BaseGridView в Designer / code: public BaseGridView<T> MyGridView { get; set; }.Проблема в том, что это не работает :-) При компиляции я получаю следующее сообщение:

Не удалось найти тип или имя пространства имен 'T' (вы пропустили директиву using илиссылка на сборку?)

Решения?

Я понимаю, что могу извлечь общую функциональность для интерфейса, пометить BaseGridView как реализацию этого интерфейса и затем обратиться к созданному интерфейсупод моим контролем.

Но мне любопытно, существует ли какая-то таинственная команда / синтаксис C #, которая помогла бы мне достичь того, чего я хочу - без загрязнения моего решения интерфейсом, который мне на самом деле не нужен: -)

РЕДАКТИРОВАТЬ : Для справки, я попробовал этот невинный обходной путь: BaseGridView<object> MyGridView { get; set; }, и ... это все еще не ответ: Не удается неявно преобразовать тип 'InvoiceGridView' в 'BaseGridView '.

Частичный успех (правка 2)

Хорошо, поскольку ковариация поддерживается только на интерфейсах, я признал поражение и определил интерфейс (только показывая некоторые из них):

public interface IBaseGridView<out T> where T : class
{
    bool ScrollTo(Predicate<T> criteria);
    bool ScrollTo(T object);
}

Теперь я могу разыграть моего любимого InvoiceGridView в IBaseGridView<object> - , что просто потрясающе , и я снова счастливый мальчик :-) Однако второй ScrollTo доставляет мне проблемы при компиляции:

Недопустимая дисперсия: параметр типа 'T' должен быть противоположно допустимым для 'GeParts.Controls.IBaseGridView.ScrollTo (T)'.'T' является ковариантным.

Теперь мне нужно изменить подпись на ScrollTo(object o) - что не идеально, но выполняет свою работу.Что меня удивило, так это то, что компилятор пожаловался на второй ScrollTo, но был доволен первым.Таким образом, кажется, что нельзя передавать экземпляров из out T, но использование самого типа (например, в Predicate<T>) нормально?Кажется довольно придирчивым ...

Ответы [ 5 ]

1 голос
/ 18 июня 2010

Так как вы написали

Но мне любопытно, существует ли какая-то таинственная команда / синтаксис C #, которая бы помогла мне достичь того, чего я хочу

Я бы хотелдобавить, что C # 4.0 позволяет заменять производные типы базовым типом, используя для ковариации.Таким образом, вы можете сделать

public BaseGridView <<strong> Object > MyGridView {get;задавать;}

Таким образом, вы получаете хорошо известный тип, но вы можете вернуть любой BaseGridView, какой захотите.К сожалению, единственный улов - ковариация разрешена только для интерфейсов!:(

1 голос
/ 18 июня 2010

C # не поддерживает общие свойства, насколько мне известно. Вы можете либо создать универсальные методы, либо включить универсальный тип в определение класса.

Например:

public BaseGridView<T> GetMyGridView<T>() { ... }
public void SetMyGridView<T>(T gridView) { ... }

или

class MyClass<T> {
    public BaseGridView<T> MyGridView { get; set; }
}
0 голосов
/ 18 июня 2010

Я только что попробовал собрать некоторый код, и он прекрасно работает для меня:

    public class A<T> where T : class
    {
        public virtual A<T> ARef
        {
            get { return default(A<T>); }
        }
    }

    public class B : A<B>
    {
        public override A<B> ARef
        {
            get
            {
                return base.ARef;
            }
        }
    }
0 голосов
/ 18 июня 2010

Вероятно, будет работать следующее:

public BaseGridView<T> MyGridView<T> { get; set; }

Проблема с вашим исходным ответом заключается в том, что параметр типа должен появляться в объявлении метода или класса, а не только в возвращаемом значении.1005 * Обратите внимание, что компилятор не может выводить универсальные типы из возвращаемых значений, поэтому вам потребуется указывать T при каждом вызове MyGridView.

0 голосов
/ 18 июня 2010

Не должно ли быть так:

public BaseGridView MyGridView {get;задавать;}

public BaseGridView<T> GetMyGridView<T> { return whatever; }
public void SetMyGridView<T>( BaseGridView<T> bgv) { whatever = bgv; }

??

Отредактировано.Мэтью прав, свойства не могут быть общими.Вы должны использовать геттер / сеттер.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...