Члены универсального класса в C #?

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

    public class ConfigSetting<T> {
    private T value;

    public T GetValue() {
        return value;
    }
    public void ChangeValue() {

    }

    public ConfigSetting(string heading, string key) {
        this.value = DerivedMethods.configsettings.SettingGroups[heading].Settings[key].RawValue;
    }
}

Тип, возвращаемый правой стороной строки this.value, в настоящее время является строкой. Я знаю, что мне кажется, что мне не нужно использовать ничего, кроме строкового типа, но в конечном итоге я расширю конструктор, так что this.value может быть строкой, int, float или bool.

В любом случае мой компилятор говорит: «Невозможно преобразовать 'string' в 'T'», поэтому я предполагаю, что делаю что-то очень обратное.

Спасибо.


person Xenoprimate    schedule 05.01.2010    source источник


Ответы (8)


Ну, какое преобразование вы ожидали от него? Если вы ожидаете, что значение уже будет правильного типа, вы можете сделать:

object tmp = DerivedMethods.configsettings.SettingGroups[heading].Settings[key].RawValue;
this.value = (T) tmp;

Обратите внимание, что вы должны пройти object либо неявно (как здесь), либо с явным приведением:

this.value = (T)(object) DerivedMethods.configsettings... (etc);

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

person Jon Skeet    schedule 05.01.2010
comment
Спасибо. Это компилируется (хотя я еще не тестировал). У меня два вопроса: 1: Почему я должен проходить через объект? 2: Что произойдет после этого, если я попытаюсь использовать методы String для значения? Знает ли теперь C #, что значение имеет тип String? - person Xenoprimate; 05.01.2010
comment
не RawValue всегда возвращает строку? не произойдет ли преобразование в int, long и т. д.? - person bendewey; 05.01.2010
comment
Да, но, как я уже сказал в своем первоначальном вопросе, я намерен расширить конструктор. - person Xenoprimate; 05.01.2010
comment
@Motig: Нет, C # не будет знать, что это строка типа - это тип T. Причины, по которым другие преобразования не разрешены, довольно сложны, и я бы не стал утверждать, что понимаю их все, но в целом, если компилятор жалуется, что преобразование недоступно, перейдите через object :) - person Jon Skeet; 05.01.2010
comment
@ skeet-lol в комментарии, очень классный способ выразить это. но в целом, если компилятор жалуется, что преобразование недоступно, перейдите через объект :). То же самое и с женатыми людьми: если жена жалуется на то, что у нее нет того или другого, чтобы избавиться от жалоб, просто передайте ей свою зарплату! - person JonH; 05.01.2010

Вы сталкиваетесь с проблемами, потому что это не лучший вариант использования дженериков. Если параметр универсального типа может быть сконструирован только четырьмя различными способами - строка, число с плавающей запятой, bool и int, то это не очень универсальный. Я ожидаю, что общая вещь может быть любого типа.

Если бы у меня была вещь, которая могла быть только одного из четырех типов, я бы смоделировал ее так:

abstract class ConfigSetting
{ /* shared code here */  }

class TextSetting : ConfigSetting
{ /* Code here to handle string settings */ }

class BooleanSetting : ConfigSetting
{ /* ... 

и так далее. Я бы, вероятно, тогда дал каждому из них внутренний конструктор и превратил базовый класс в factory для производных классов, используя шаблон factory.

Используйте универсальные шаблоны только в том случае, если ваше решение действительно универсальное. Подобно List<T>, например, это может быть список чего угодно: целых чисел, строк, массивов, словарей, функций и т. Д. Если модель, которую вы моделируете, имеет небольшое количество возможных типов, просто сделайте по одной для каждого типа.

person Eric Lippert    schedule 05.01.2010

Вам нужно преобразовать возвращенную строку в T:

public ConfigSetting(string heading, string key) {
    this.value = (T)DerivedMethods.configsettings.SettingGroups[heading].Settings[key].RawValue;
}
person Oded    schedule 05.01.2010
comment
Это не работает, та же ошибка. Я бы сказал, что уже пробовал это, но я догадался, что проблема глубже, чем это. - person Xenoprimate; 05.01.2010

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

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

Я вижу, что другие уже предоставили кодовые ответы, поэтому я остановлюсь на этом.

person John K    schedule 05.01.2010

Я предполагаю что

DerivedMethods.configsettings.SettingGroups[heading].Settings[key].RawValue

это строка.

Вы не можете присвоить строку this.value, потому что тип this.value - T и может быть любым, например типами, которым строки не могут быть присвоены.

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

public float GetFloatValue {
    get { return float.Parse(this.value); }
}

// etc
person recursive    schedule 05.01.2010

Похоже, вы пытаетесь назначить строку (configsettings.SettingGroups[heading].Settings[key].RawValue) универсальному члену. Вам нужно будет каким-то образом преобразовать строку в тип T - обычно с помощью приведения. Например:

this.value = (T)DerivedMethods.configsettings.SettingGroups[heading].Settings[key].RawValue;
person Justin Rusbatch    schedule 05.01.2010

В этой строке:

this.value = DerivedMethods.configsettings.SettingGroups[heading].Settings[key].RawValue;

вы устанавливаете для переменной типа T строковое значение. Метод RawValue возвращает строку. Вам нужно явно привести его к типу T.

this.value = (T) (object) DerivedMethods.configsettings.SettingGroups[heading].Settings[key].RawValue;

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

person Patrick Karcher    schedule 05.01.2010

Попробуйте

string strValue = DerivedMethods.configsettings.SettingGroups[heading].Settings[key].RawValue;
this.value = (T)Convert.ChangeType(strValue, typeof(T), CultureInfo.InvariantCulture)

если у вас есть значения конфигурации, хранящиеся в виде строк, и вы хотите, чтобы они были преобразованы в правильный тип. Это работает только для большинства примитивных типов, таких как Int32, Double и т. Д.

person Rauhotz    schedule 05.01.2010