采用默认值,而不是设置try catch块

时间:2019-05-02 08:09:13

标签: c#

我必须查询一些可能为空的数据,并在访问它们时引发异常。假设我必须查询一个整数字段

int[] numbers = { 0, 1, 2, 3, 4 };
int targetNumber = numbers[10];

我必须将int targetNumber = numbers[10];包装到try catch块中,因为索引将抛出超出范围的异常。

int targetNumber;

try
{
    targetNumber = numbers[10];
}
catch (Exception)
{
    targetNumber = 7; // default value for index 10
}

当加载多个字段时,这将是非常低效的。我正在寻找一种使用这种方式的方法

int targetNumber = numbers[10] || 7;

引发异常时,应用程序将不会崩溃并采用默认值。通过这种方法,我可以避免多个try catch块。

整数数组只是一个例子。我在考虑带有复杂对象的复杂集合。因此,外部库可以使我访问ICollection类型的集合,并且可以使用字符串(键)访问这些集合。如果密钥不存在,则会引发异常。

因此,当使用

访问这些文件时
string value = collection["myKey"]

我想将其扩展到

string value = collection["myKey"] || "MyDefaultValueToTakeIfSomethingFailed";

3 个答案:

答案 0 :(得分:9)

关于最近,我可以想到的是,如果要测试数组的长度以确保其包含索引10(第11个项目)的元素,请使用内联

declare @r table(Product_Code varchar(20),Product_Description varchar(20),Min_Range decimal(10,2),Max_Range decimal(10,2),Interest_Rate decimal(10,2));
insert into @r values('2000-0100','Saving',0,4999.99,0.01),('2000-0100','Saving',5000,9999.99,0.02),('2000-0100','Saving',10000,49999.99,0.03),('2000-1111','Senior Savings',0,4999.99,0.03),('2000-1111','Senior Savings',5000,9999.99,0.04),('2000-1111','Senior Savings',10000,49999.99,0.05);

declare @b table(BalanceDate date,Balance decimal(10,2),Product_Code varchar(20),Product_Description varchar(20),AccountNo int);
insert into @b values('20190228',19447.83,'2000-0100','Saving',3059123),('20190227',19557.61,'2000-0100','Saving',3059123),('20190226',19976.01,'2000-0100','Saving',3059123),('20190225',20530.91,'2000-0100','Saving',3059123),('20190228',12345,'2000-1111','Senior Savings',4059123),('20190227',5456,'2000-1111','Senior Savings',4059123),('20190226',9999,'2000-1111','Senior Savings',4059123),('20190225',7893,'2000-1111','Senior Savings',4059123);

select b.AccountNo
      ,b.Product_Code
      ,b.Product_Description
      ,b.BalanceDate
      ,b.Balance
      ,r.Min_Range
      ,r.Max_Range
      ,r.Interest_Rate
      ,case when b.Balance > r.Max_Range
            then r.Max_Range - r.Min_Range
            else b.Balance - r.Min_Range
            end as Split_Balance
      ,r.Interest_rate * case when b.Balance > r.Max_Range
                            then r.Max_Range - r.Min_Range
                            else b.Balance - r.Min_Range
                            end as Split_Balance_Interest
from @b as b
    join @r as r
        on b.Product_Code = r.Product_Code
            and b.Balance > r.Min_Range
order by b.AccountNo
        ,b.BalanceDate;

不是存在空引用,而是您的数组长度不足以提供索引10(ArrayIndexOutOfBoundsException)

基本规则是“如果数字数组的长度大于X的X处的检索元素”-如果有11个或更多的元素,则检索[10]将会成功

如果您使用的是可为空的内容,那么您还可以使用??运营商提供默认值

int targetNumber = numbers.Length > 10 ? numbers[10] : 7;

您不能完全避开这两个不同的例外。 ArrayIndexOutOfBounds和NullReferenceexception-长度测试可防止越界,然后可以使用??防止空值(可能是元素10为空或数组不够长)。

答案 1 :(得分:7)

您可以使用LINQ:

string targetString = (strings.Length > 10 ? strings[10] : null)  ?? "defaultstring";

int targetNumber = numbers.Cast<int?>().ElementAtOrDefault(10) ?? 7; 将使您从中选取的元素为空,这将使Cast返回ElementAtOrDefault,以防您未击中条目。最后,如果合并表达式为null,则空合并运算符??将使表达式成为默认值。

如果数组包含引用类型或可为空的值类型,则可以不进行强制转换:

null

在这种情况下,您可以期望string targetString = strings.ElementAtOrDefault(10) ?? "default"; 在基础对象中检查ElementAtOrDefaultIList<T>,因此它不会在此处迭代整个数组,而是评估其长度并选择直接从其索引中获取价值。

答案 2 :(得分:1)

您可以使用适用于各种数据类型的具有相同签名的扩展方法来构建taket。编译器将在编译时选择合适的编译器,因此在运行时性能将达到最佳。

phoneNumber?.trimmingCharacters(in: .symbols).count

用法示例:

public static class GetValueExtensions
{
    public static T GetValue<T>(this T[] source, int key, T defaultValue) // Array
    {
        if (key >= 0 && key < source.Length) return source[key];
        return defaultValue;
    }

    public static T GetValue<T>(this IList<T> source, int key, T defaultValue) // List
    {
        if (key >= 0 && key < source.Count) return source[key];
        return defaultValue;
    }

    public static TValue GetValue<TKey, TValue>( // Dictionary
        this IDictionary<TKey, TValue> source, TKey key, TValue defaultValue)
    {
        if (source.TryGetValue(key, out TValue value)) return value;
        return defaultValue;
    }
}

如果要避免一次又一次重复默认值,则可以使用:

var s = new string[] { "Foo", "Bar" }.GetValue(5, "Empty"); // Returns "Empty"
var c = new List<char> { 'a', 'b', 'c' }.GetValue(2, '_'); // Returns 'c'
var b = new Dictionary<int, bool>() { { 1, false }, { 2, true } }
    .GetValue(13, true); // Returns true

用法示例:

public static Func<int, T> GetGetter<T>(this T[] source, T defaultValue)
{
    return (key) =>
    {
        if (key >= 0 && key < source.Length) return source[key];
        return defaultValue;
    };
}