4

是否可以在 C# 中以某种方式调用内联try语句?

我正在为我的网站检测语言,有时,当语言en-GR出于某种原因在客户端时,.NET 会引发异常。所以我需要使用trycatch即使我没有真正捕捉到任何东西。

在这种情况下,这似乎完全是矫枉过正。

// Set allowed languages
string[] allowedLanguages = { "en", "fr", "ru" };

// Get all possible values
var routeLanguage = (filterContext.RouteData.Values["lang"] != null && allowedLanguages.Contains(filterContext.RouteData.Values["lang"].ToString())) ? filterContext.RouteData.Values["lang"].ToString() : null;
var cookieLanguage = (filterContext.HttpContext.Request.Cookies["lang"] != null && allowedLanguages.Contains(filterContext.HttpContext.Request.Cookies["lang"].Value)) ? filterContext.HttpContext.Request.Cookies["lang"].Value : null;
string clientLanguage = null;
try
{
    clientLanguage = (filterContext.HttpContext.Request.UserLanguages != null) ? new CultureInfo(filterContext.HttpContext.Request.UserLanguages[0]).TwoLetterISOLanguageName : null; // Exception sometimes without `try`
}
catch (Exception)
{
}

编辑

异常不是我可以解决的,因为我无法控制用户在其文化信息中的内容。.NET 只是将 en-FR 视为无效的。

4

8 回答 8

15

首先,最好首先弄清楚如何避免异常。首先集中精力。抛出该异常是有原因的,如果您可以确定它是什么,请不要这样做。

要真正回答您的问题:没有开箱即用的“吃掉这个表达式中的所有异常”机制,但是构建自己的机制很简单:

static T EatExceptions(Func<T> func)
{
  try { return func(); } catch { }
  return default(T);
}
...
clientLanguage = (filterContext.HttpContext.Request.UserLanguages != null) ? 
  EatExceptions(() => new CultureInfo(filterContext.HttpContext.Request.UserLanguages[0]).TwoLetterISOLanguageName) :
  null; }

如果有人试图在我正在审查的代码中进行这样的恶作剧,那么我会......好吧,我们只是说更改不会被签入。像这样吃异常在 99% 的情况下都是一个非常糟糕的主意。再次:找出你做错了什么并停止这样做。不要做错事,然后处理失败。

于 2013-02-09T16:34:11.703 回答
7

您是否尝试过完全摆脱该try/catch声明?

string clientLanguage = null;
var userLanguages = filterContext.HttpContext.Request.UserLanguages;
if (userLanguages != null && userLanguages.Length > 0)
{
    var culture = CultureInfo
        .GetCultures(CultureTypes.AllCultures)
        .FirstOrDefault(
            x => string.Equals(
                x.Name, 
                userLanguages[0].Name, 
                StringComparison.OrdinalIgnoreCase
            )
        );
    if (culture != null)
    {
        clientLanguage = culture.TwoLetterISOLanguageName;
    }
}

仅使用 try/catch 来处理您无法控制的异常。顾名思义,应该使用异常来处理异常情况。

在这种情况下,您正在执行标准解析,因此最好进行防御性编程,而不是尝试、投掷、捕捉……

于 2013-02-09T16:30:23.417 回答
4
_.Try(() => __YourStatementHere__ );

使用这样的小助手类:

/// <summary>
/// Other utility functions.
/// </summary>
public static class _ {

    /// <summary>
    /// Tries to execute the given action. If it fails, nothing happens.
    /// </summary>
    public static void Try(Action action) {
        try {
            action();
        }
        catch {
        }
    }

}

我知道,这个解决方案也不是最优的,但到目前为止我能找到的最简洁的一个。

于 2019-08-30T21:12:30.597 回答
1

首先尽量避免异常。仅仅因为字符串来自您无法控制的来源,并不意味着您无法验证它。

如果无法避免,则应捕获您期望的特定异常并将该逻辑封装在方法中。不要捕获所有异常。

例如:

public static CultureInfo TryGetCultureByName(string name)
{
   try
   {
     return new CultureInfo(name);
   }
   catch(CultureNotFoundException)//Only catching CultureNotFoundException
   {
     return null;
   }
}

这样,如果您以后发现处理此特定错误的更好方法,您可以轻松替换它。

例如,您可以创建一个Dictionary<string, CultureInfo>,填充它CultureInfo.GetCultures()并使用它TryGetValue来查找文化,而不会引发异常。

于 2013-02-09T16:39:08.740 回答
1

你所做的是正确的方法。你说,为什么你不能摆脱异常(我认为是这种情况)。所以你必须处理它。唉,C# 没有 try-catch 作为表达式(不确定它是如何工作的 - catch“子句”需要返回一个值)。

或者,您可以构建一个小辅助函数,该函数接受 a Func<T>,调用它并将值传递给调用者。如果发生异常,则返回(例如)default(T)。这消除了很多混乱并且可以重复使用。

于 2013-02-09T16:34:01.010 回答
0

好吧,撇开关于预先检查的(好的)建议不谈,有几种相当平凡/平淡/明显的方法可以做到这一点:

首先,您可以将其包装在一个函数中。我认为这对您来说不够笼统。

或者,您可以折叠catch分支:

try
{
    clientLanguage = (filterContext.HttpContext.Request.UserLanguages != null) ? new CultureInfo(filterContext.HttpContext.Request.UserLanguages[0]).TwoLetterISOLanguageName : null; // Exception sometimes without `try`
} catch (Exception) { }

或者,您可以将整个内容折叠成一行:

try { clientLanguage = (filterContext.HttpContext.Request.UserLanguages != null) ? new CultureInfo(filterContext.HttpContext.Request.UserLanguages[0]).TwoLetterISOLanguageName : null; } catch (Exception) { }

不优雅,但简单,并且有效。

于 2013-02-09T16:34:14.673 回答
0

这是另一种方式:

await new @try(async () => { user = await GetItemAsync(userId); })
         .@catch<Exception>(async () => { user = new User(); });

我认为它比 try/catch 更冗长:

try { return await GetItemAsync(userId); } 
catch (Exception ex) { return new User(); }

助手类:

public class @try
{
    private Func<Task> func;
    private Action action;

    public @try(Func<Task> func)
    {
        this.func = func;
    }

    public @try(Action action)
    {
        this.action = action;
    }

    public async Task @catch<T>(Func<Task> fallback) where T : Exception
    {
        try
        {
            await func.Invoke();
        }
        catch(T ex)
        {
            await fallback.Invoke();
        }
    }

    public void @catch<T>(Action fallback) where T : Exception
    {
        try
        {
            action.Invoke();
        }
        catch (T ex)
        {
            fallback.Invoke();
        }
    }
}
于 2021-10-31T16:44:31.373 回答
-2

我已经建立了一个可能适合此目的的在线尝试捕获机制。我想要一种类似 LINQ 或 Callback function-y 的语法。这是使用几个包装器,一个 TryWrapper 和一个 CatchWrapper 来完成的,以便点运算符适当地提示下一步,隐式转换为 T 类型。

例子

你也可以做类似的事情

Try(() => UpdateSweetGreen("21", SweetGreen))
.Catch(LogToDb(e.Message))
.Catch(LogToFile(e.Message).Finally(ReportNewSweetGreen(SweetGreen);

基本上 CatchWrapper 扩展了 TryWrapper。因此,您可以从另一个 catch 块中捕获异常。在这种情况下,将您的方法失败记录到数据库中,然后如果失败记录到文件中,那么无论向其他组件报告 SweetGreen 变量是什么。

这一切都从 TryWrapper 扩展而来

    public class TryWrapper<T>
    {
        protected internal T Result { get; set; } = default(T);

        protected internal Exception Exception { get; set; } = null;

        public static implicit operator T(TryWrapper<T> wrapper)
        {
            return wrapper.Result;
        }

        public static implicit operator Exception(TryWrapper<T> wrapper)
        {
            return wrapper.Exception;
        }
    }

和 CatchWrapper 它只是扩展 TryWrapper 并且不能直接调用,而是仅在尝试后出现,正如您在标准实现中所期望的那样

    public class CatchWrapper<T> : TryWrapper<T>
    {
    }

然后是一系列静态扩展方法

        public static TryWrapper<T> Try<T>(Func<T> func)
        {
            var product = new TryWrapper<T>();

            try
            {
                product.Result = func.Invoke();
            }
            catch (Exception e)
            {
                product.Exception = e;
            }

            return product;
        }

        public static TryWrapper<T> Try<T>(Action action)
        {
            var product = new TryWrapper<T>();

            try
            {
                action.Invoke();
            }
            catch (Exception e)
            {
                product.Exception = e;
            }

            return product;
        }

        public static CatchWrapper<T> Catch<T>(this TryWrapper<T> wrapper, Action<Exception> response)
        {
            if (wrapper.Exception is null) return wrapper as CatchWrapper<T>;

            response.Invoke(wrapper);
            wrapper.Exception = null;

            return wrapper as CatchWrapper<T>;
        }


        public static TryWrapper<T> Finally<T>(this TryWrapper<T> wrapper, Action<T> response)
        {
            response.Invoke(wrapper);

            return wrapper;
        }

        public static TryWrapper<T> Finally<T>(this TryWrapper<T> wrapper, Func<T> response)
        {
            wrapper.Result = response.Invoke();

            return wrapper;
        }

        public static TryWrapper<T> Finally<T>(this TryWrapper<T> wrapper, Action response)
        {
            response.Invoke();

            return wrapper;
        }

现在这确实实现了 OP 要求的内联语法,但我敢说它的效率较低,因为您可以直接在标准的 try-catch 中处理异常。能够在 Try 之前直接指定 return 仍然有点酷,尽管如果默认为 null,这是有风险的。

于 2020-01-07T17:37:32.503 回答