Подтвердить что ты не робот

Как использовать <T>.TryParse в общем методе, в то время как T является либо двойным, либо Int

в одном из моих проектов я использую следующие два метода. 1. GetDoubleValue и 2. GetIntValue. GetDoubleValue использует double.TryParse для параметра str string и возвращает 0, если он терпит неудачу, в то время как GetIntValue пытается int.TryParse параметру str string и возвращает 0, если он терпит неудачу. Я хочу объединить эти два метода в один общий метод, который вместе с строкой str также получает параметр T, поэтому, если я хочу использовать метод GetDoubleValue, я могу использовать double для параметра T, и если я хочу использовать метод GetIntValue, я могу использовать Int для параметра T

public double GetDoubleValue(string str)
{
    double d;
    double.TryParse(str, out d);
    return d;
}
public int GetIntValue(string str)
{
    int i;
    int.TryParse(str, out i);
    return i;
}

Примечание. Я пробовал что-то вроде этого:

private T GetDoubleOrIntValue<T>(string str) where T : struct 
{
    T t;
    t.TryParse(str, out t);
    return t;
}

ИЗМЕНИТЬ

В моей базе данных у меня более 30 столбцов в разных таблицах с числовым типом данных. Я хочу вставить 0 в каждый столбец, если пользователь не вводит ничего в текстовое поле. Если он оставляет все или некоторые текстовые поля пустыми. Если я не использую метод GetIntValue, мне придется использовать тело метода более 30 раз. поэтому я делаю это с помощью метода. Например, я пишу три из более чем тридцати примеров

cmd.Parameters.Add("@AddmissionFee", SqlDbType.Decimal).Value = GetIntValue(tbadmissionfee.Text);
cmd.Parameters.Add("@ComputerFee", SqlDbType.Decimal).Value = GetIntValue(tbcomputerfee.Text);
cmd.Parameters.Add("@NotesCharges", SqlDbType.Decimal).Value = GetDoubleValue(tbnotescharges.Text);

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

4b9b3361

Ответ 1

Вы можете сделать что-то вроде этого:

   public static T GetDoubleOrIntValue<T>(this string str) where T : IConvertible
{
    var thisType = default(T);
    var typeCode = thisType.GetTypeCode();
    if (typeCode == TypeCode.Double)
    {
        double d;
        double.TryParse(str, out d);
        return (T)Convert.ChangeType(d,typeCode) ;
    }
    else if (typeCode == TypeCode.Int32)
    {
        int i;
        int.TryParse(str, out i);
        return (T)Convert.ChangeType(i, typeCode);
    }
    return thisType;
}

Затем, когда вы его вызываете:

string d = "1.1";
string i = "3";

double doubleValue = d.GetDoubleOrIntValue<double>();
int intValue = i.GetDoubleOrIntValue<int>();

Но все это кажется мне глупым.

РЕДАКТИРОВАТЬ: Видел кого-то другого с помощью Convert.ChangeType..., который предусматривает общий тип возвращаемого значения.

Ответ 2

Я согласен с Марком Байерсом. Вероятно, не стоит пытаться сделать этот метод общим. Небольшое дублирование кода не повредит (пока это действительно немного). Тот факт, что вы можете использовать любой struct с вашей общей версией, также не делает его для меня хорошей идеей.

Если вы действительно хотите это сделать, вы можете попробовать использовать отражение (как предположил Минустар), но это было бы как уродливо, так и медленнее.

Вместо этого вы можете использовать Convert.ChangeType():

private T GetValue<T>(string str) where T : struct 
{
    return (T)Convert.ChangeType(str, typeof(T));
}

Ответ 3

Я бы подумал о написании расширения или статического метода:

delegate bool TryParse<T>(string str, out T value);

public static T GetValue<T>(this string str, TryParse<T> parseFunc)
{
    T val;
    parseFunc(str, out val);
    return val;
}

Затем вам необходимо предоставить реализацию TryParse, которую вы хотите использовать:

int i = "1234".GetValue<int>(int.TryParse);

Будем предупреждать, что эта функция молча возвращает значение по умолчанию, если синтаксический анализ не выполняется. Вы можете вернуть default(T), если делегат TryParse возвращает false.

Ответ 4

Итак, вместо написания:

double d = GetDoubleValue(str);

Вы хотите написать это?

double d = GetValue<double>(str);

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

Вы не одиноки с этой проблемой. Взгляните на другие методы в .NET framework, которые работают на разных типах и видят, как они его решили. Здесь BinaryReader позволяет вам читать разные типы:

Это не очень, но так, как обычно это делается.


Что касается вашего обновления, у меня есть еще два момента.

Вы упомянули, что у вас, вероятно, будет более двух типов, и это может привести к большему дублированию одного и того же кода. Учитывая, что метод очень краткий и простой, не представляется проблемой для копирования и вставки кода. Разумным исключением из правила является то, что вы не должны копировать и вставлять код, на мой взгляд. Подход здесь уменьшает количество дублирования, хотя вам все равно нужно много одинаково названных методов в вашем общедоступном интерфейсе.

Однако я считаю важным упомянуть, что кажется неправильным использовать значение по умолчанию в вашей ситуации, как уже упоминал Ли в комментарии. Если пользователь совершает ошибку при вводе данных, они должны получить сообщение об ошибке. Вы должны использовать некоторую структуру проверки, чтобы убедиться, что строки верны и сообщить вашему пользователю, в чем проблема, если они недействительны. После того, как вы проверили все, можно использовать int.Parse вместо int.TryParse, потому что вы знаете, что синтаксический анализ будет успешным. И если синтаксический анализ завершается неудачей, это исключительная ситуация (ошибка в вашей проверке), поэтому кажется справедливым, что приложение выдает исключение и регистрирует трассировку стека. Это поможет вам найти и исправить ошибку.

Ответ 5

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

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine(GetValue<int>("123"));
        Console.WriteLine(GetValue<double>("123.123"));
        Console.WriteLine(GetValue<DateTime>("2001-01-01 01:01:01"));
    }

    static T GetValue<T>(string s)
    {
        var tryParse = typeof (T).GetMethod(
            "TryParse", new [] {typeof(string), typeof(T).MakeByRefType()});
        if (tryParse == null)
            throw new InvalidOperationException();


        T t = default (T);
        var parameters = new object[] {s, t};
        var success = tryParse.Invoke(null, parameters);
        if ((bool) success) t = (T)parameters[1];
        return t;
    }
}

Ответ 6

Вы можете попробовать с отражением: сначала попробуйте найти метод TryParse для типа, замененного на <T> .

T t;
var typeInfo = typeof(T);
var method = typeInfo.GetMethod("TryParse", BindingFlags.Public | BindingFlags.Static);
var result = (bool)method.Invoke(null, new object[] { str, t });

if (result)
    return t;

Этот код не проверен; и он не является оптимальным по производительности.

Ответ 7

Как вы видите, вы не можете ограничить where T на Double или Int32, поэтому у вас, вероятно, есть struct на этом месте (int и double не являются struct). Типы значений не могут использоваться в качестве ограничений для генериков.

Таким образом, вы не можете вводить безопасный метод, чтобы существовал метод TryParse. Вы должны либо typecheck T, либо исключить исключение, если он либо double, либо int, как в ответ на @ie.

Мое предложение - использовать другую подпись:

private static bool GetValue(string str, out double result)
{
...    
}

private static bool GetValue(string str, out int result)
{
...    
}

В качестве альтернативы вы можете ввести интерфейс IParsable. Box Double или int приводит к нему и реализует неявное преобразование в двойное или целое.

Ответ 8

Как насчет метода расширения?

public static class Extensions
{    
    public static Nullable<T> TryParse<T>(this String str) where T:struct
    {
        try
        {
            T parsedValue = (T)Convert.ChangeType(str, typeof(T));
            return parsedValue;
        }
        catch { return null; }
    }
}

использование:

int i = "123".TryParse<int>() ?? 0;