如何在泛型方法中使用<t> .TryParse,而T是double或Int </t>

时间:2012-05-13 19:19:31

标签: c# generics

在我的一个项目中,我正在使用以下两种方法。 1. GetDoubleValue和2. GetIntValue。 GetDoubleValue使用double.TryParse参数str字符串,如果失败则返回0,而GetIntValue尝试int.TryParse到参数str字符串,如果失败则返回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;
}

修改

在我的数据库中,我在具有numeric数据类型的不同表中有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等希望它现在已经清除,为什么我要这个?

8 个答案:

答案 0 :(得分:10)

我会考虑编写扩展或静态方法:

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)委托返回false,您可能希望返回TryParse

答案 1 :(得分:9)

我同意Mark Byers的观点。尝试将此方法设为通用可能不是一个好主意。一点点代码重复不会受到伤害(只要它真的只是一点点)。您可以将任何struct与您的通用版本一起使用的事实也不会让我看起来像个好主意。

如果你真的想这样做,你可以尝试使用反射(如Minustar建议的那样),但那既丑又慢。

相反,您可以使用Convert.ChangeType()

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

答案 2 :(得分:7)

所以不要写:

double d = GetDoubleValue(str);

你想写这个吗?

double d = GetValue<double>(str);

即使你可以让它发挥作用,有什么好处?我个人并不认为这对客户来说是一个巨大的进步。唯一的优点是不必两次实现相同的方法。但鉴于该方法的简单性以及实现此类代码重用的难度,这里只复制那几行代码似乎是合理的。

这个问题并不孤单。看一下.NET框架中适用于不同类型的其他方法,看看它们是如何解决的。以下是BinaryReader允许您阅读不同类型的方式:

它不漂亮,但这就是通常的方式。


关于您的更新,我还有两点要做。

您提到您可能会有两种以上的类型,这可能导致相同代码的重复更多。鉴于该方法非常简洁和简单,复制和粘贴代码似乎不是问题。在我看来,不应该复制和粘贴代码的规则是一个合理的例外。方法here减少了重复量,但在公共接口中仍需要许多类似命名的方法。

但是我认为重要的是要提到在你的情况下默默使用默认值似乎是错误的,正如Lee在评论中已经提到的那样。如果用户在输入数据时出错,则应收到错误消息。您应该使用某种验证框架来确保字符串有效并告知用户如果它们无效则问题是什么。验证完所有内容后,使用int.Parse代替int.TryParse是安全的,因为您知道解析会成功。如果解析失败,这是一种特殊情况(验证中的错误),因此应用程序通过异常退出并记录堆栈跟踪似乎是公平的。这将帮助您找到并修复错误。

答案 3 :(得分:6)

你可以这样做:

   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看到其他人...这提供了一般的返回类型。

答案 4 :(得分:1)

您可以尝试使用反射:首先,尝试在&lt; T&gt;替换的类型上找到“TryParse”方法。

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;

此代码未经过测试;它也不是最佳的性能。

答案 5 :(得分:1)

我做了一个简单的样本。代码不是最佳的,但它按预期工作。

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 :(得分:0)

如您所见,您无法将where T限制为DoubleInt32,这就是您在该地点可能有struct的原因(int和double不是结构)。值类型不能用作泛型的限制。

因此,您无法输入安全性,请确保存在TryParse方法。您应该检查T并检查异常,如果它是double或int,就像在@ie中回答一样。

我的建议是使用不同的签名:

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

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

或者,您可以引入界面IParsable。框doubleint生成框并实现隐式转换为double或integer。

答案 7 :(得分:0)

扩展方法怎么样?

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;