Функция С# Generics

Может ли кто-нибудь объяснить это поведение в Generics?

У меня есть общая функция в С#

protected virtual void LoadFieldDataEditor <T> (ref T control, string strFieldName) where T : Control
{
  //T can be different types of controls inheriting from System.Web.UI.Control
  if (control is TextBox)
  {
   //This line gives an error
   //((TextBox)control).Text = "test";

   //This line works! 
   (control as TextBox).Text = "Test";
  }
}

На стороне примечания, могу ли я использовать случай переключения, когда я выполняю проверку типа "Control - TextBox"?

EDIT:

Забыл добавить сообщение об ошибке Извините!

Здесь вы идете:

Error   3   Cannot convert type 'T' to 'TextBox'

EDIT:

Пока мы говорим о дженериках, у меня есть другой вопрос. (Не уверен, если бы мне пришлось начать новую запись)

Этот метод был расширен, чтобы включить другой общий тип

protected virtual void LoadFieldDataEditor <T1, T2> (T1 control, T2 objData, string strFieldName) where T1 : Control where T2 : BaseDataType
{
  //I will need to access field1. 
  //I don't know at compile time if this would be SomeType1 or 
 //SomeType2 but all of them inherit from BaseDataType. 

  //Is this possible using generics?
}

public abstract class BaseDataType {}

public class SomeType1 : BaseDataType
{
   string field1;
   string field2;
}

Ответы

Ответ 1

Правила того, что общий тип может быть преобразован, являются довольно сложными, а иногда и противоречивыми, как в этом случае. Подробности см. В разделе 6.2.6 спецификации С#. Есть места, где они могут быть слабее, и я думаю, что это один из них. Вы можете отбрасывать до object, а затем снова вниз, но это уродливо.

В этом случае лучшим решением было бы следующее:

protected virtual void LoadFieldDataEditor <T> (ref T control,
                                                string strFieldName) 
    where T : Control
{
    TextBox textBox = control as TextBox;
    if (textBox != null)
    {
        textBox.Text = "test";
    }
}

Помимо всего прочего, для этого требуется только одна проверка времени выполнения вместо двух.

Для примечания стороны: нет, вы не можете использовать типы переключателей/случаев для типов. (Вы можете получить имя типа и включить его, но это было бы ужасно.)

Ответ 2

Я бы очень рекомендовал рефакторинг:

protected virtual void LoadFieldDataEditor(Control control, string strFieldName) 

Как упоминалось в нескольких комментариях, этот метод вообще не нуждается в генериках.

Поскольку вы ограничены элементом управления, вы знаете базовый класс и это ссылочный тип (Control), поэтому вы можете избежать создания дженериков и объявления параметра ref.

Так как Control является ссылочным типом, вы можете изменить его свойства в методе, и это будет работать правильно. Настройка .Text и т.д. Сделает именно то, что вы пытаетесь сделать, но будет намного проще.

Есть небольшой шанс, что вам может понадобиться:

protected virtual void LoadFieldDataEditor(ref Control control, string strFieldName) 

но это потребовалось бы только в том случае, если вы собираетесь переназначить управление внутри своего метода (т.е. control = new TextBox();). Я бы настоятельно рекомендовал не делать этого, поскольку это может вызвать какое-то очень неожиданное поведение и не будет очевидным. Если вы пытаетесь создать новый элемент управления, используя параметр out или просто вернув новый элемент управления, все станет более понятным.

Кроме того, в целом рекомендуется избегать общих методов, если нет веских оснований для их включения. Команда FxCop добавила (и позже удалила) некоторые правила, пытаясь обескуражить их использование, поскольку они, как правило, делают код менее понятным в долгосрочной перспективе. Существует много веских причин использования общих методов, но это не требует их использования, поэтому я бы рекомендовал их избегать.

Ответ 3

Первая строка дает ошибку компилятора: "Невозможно преобразовать тип T в TextBox". Такой тип действия является законным, если компилятор может знать, что можно преобразовать начальный класс в конечный класс. Поскольку T может быть чем угодно, нет возможности узнать компилятора. Несмотря на то, что вы проверяете время выполнения, это не успокаивает компилятор. Второй вид акта в порядке, потому что он просто вернет null, если актер не работает. EDIT: Как указывает tuinstoel, правила кастинга сложнее, чем я описывал.

Ответ 4

В ответ на @rossfabricant.

Это не так просто, первый метод компилируется, второй - нет.

void Test(Control control)
{
    if (control is TextBox)
    {
       ((TextBox)control).Text = "test";
    }
}

void Test<T>(T control) where T : Control
{
    if (control is TextBox)
    {
        ((TextBox)control).Text = "test";
    }
}