24

在我的一个项目中,我使用了以下两种方法。1. GetDoubleValue 和 2. GetIntValue。GetDoubleValue 使用 double.TryParse 参数 str 字符串,如果失败则返回 0,而 GetIntValue 尝试 int.TryParse 参数 str 字符串,如果失败则返回 0。我想要的是将这两种方法组合成一个通用方法,该方法与字符串 str 一起接收参数 T ,这样如果我想使用 GetDoubleValue 方法,我可以使用 double 作为参数 T ,如果我想使用 GetIntValue 方法,我可以使用参数 T 的 Int

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 等。希望现在清楚我为什么要这个???

4

8 回答 8

11

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

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 ,您可能希望返回。

于 2012-05-13T19:41:38.073 回答
9

我同意马克拜尔斯的观点。尝试使此方法通用可能不是一个好主意。一点点代码重复不会有什么坏处(只要它真的只是一点点)。您可以将任何与您的通用版本一起使用的事实struct也不会使它看起来对我来说是一个好主意。

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

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

private T GetValue<T>(string str) where T : struct 
{
    return (T)Convert.ChangeType(str, typeof(T));
}
于 2012-05-13T19:40:57.280 回答
8

所以不要写:

double d = GetDoubleValue(str);

你想写这个吗?

double d = GetValue<double>(str);

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

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

这并不漂亮,但通常就是这样做的。


关于你的更新,我还有两点要说。

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

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

于 2012-05-13T19:31:40.233 回答
8

你可以这样做:

   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... 它提供了一个通用的返回类型。

于 2012-05-13T20:34:54.807 回答
1

您可以尝试使用反射:首先,尝试在由 <T> 替换的类型上找到“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;

此代码未经测试;在性能方面也不是最佳的。

于 2012-05-13T19:31:19.483 回答
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;
    }
}
于 2012-05-13T19:39:57.873 回答
0

如您所见,您不能限制where TDoubleor Int32,这就是为什么您可能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结果放入其中并实现隐式转换为双精度或整数。

于 2012-05-13T19:40:41.937 回答
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;
于 2016-05-05T12:20:16.343 回答