你能捕获一个泛型为任意类型 T 的 Exception<T> 吗?

40
我希望能够捕获 WebFaultException<string> 作为最具体的情况,然后再捕获 WebFaultException<T>(其中 T 是任何其他类型)作为更一般的情况来处理。这种情况是否可行?
try
{
    // throw exception
}
catch (WebFaultException<string> e)
{
    // handle more specific type
}
catch (WebFaultException<T> e)
{
    // handle more general type
}
catch (Exception e)
{
}

WebFaultException<T> 有基类吗? - leppie
它的作用是:http://msdn.microsoft.com/en-us/library/system.servicemodel.faultexception.aspx - leppie
我没有在任何地方看到通用的 WebFaultException<T> ... - bitbonk
8个回答

17
正如其他答案所提到的,你无法直接指定捕获每个WebFaultException<T>,除非你知道指定的类型参数,或者捕获它的基类型。
然而,如果你想要捕获所有WebFaultException出现,并根据泛型类型不同来处理响应,则可以简单地捕获基类型并使用反射来确定泛型参数的类型,使用Type.GetGenericArguments()。这里是一个简单的catch子句,展示了你可以如何做到:
// ...

catch (FaultException ex)
{
    Type exceptionType = ex.GetType();

    if (exceptionType.IsGenericType)
    {
        // Find the type of the generic parameter
        Type genericType = ex.GetType().GetGenericArguments().FirstOrDefault();

        if (genericType != null)
        {
            // TODO: Handle the generic type.
        }
    }
}

如果您需要更深入的示例,我已经上传了一个测试程序到PasteBin以演示此功能。请随意查看!

1
WebFaultException<T>不是从WebFaultException派生的。 - Petr Behenský

5

我从未想过这种情况,因此我很好奇并进行了一些简单的实现。首先,以下内容有效:

public class WebFaultException : Exception
{
    public WebFaultException() { }
    public WebFaultException(string message) : base(message) { }
    public WebFaultException(string message, Exception innerException) : base(message, innerException) { }
    protected WebFaultException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
}

public class WebFaultException<T> : WebFaultException
{
    public WebFaultException() { }
    public WebFaultException(string message) : base(message) { }
    public WebFaultException(string message, Exception innerException) : base(message, innerException) {  }
    protected WebFaultException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) : base(info, context) {}
}

当您的异常定义和这些测试都通过时:

    [TestMethod]
    public void SpecificGeneric()
    {
        bool hitException = false;
        try
        {
            throw new WebFaultException<string>();
        }
        catch(WebFaultException<string> e)
        {
            hitException = true;
        }

        Assert.IsTrue(hitException);
    }

    [TestMethod]
    public void AnyGeneric()
    {
        bool hitException = false;
        try
        {
            throw new WebFaultException<int>();
        }
        catch (WebFaultException<string> e)
        {
            hitException = false;
        }
        catch (WebFaultException e)
        {
            hitException = true;
        }

        Assert.IsTrue(hitException);
    }

就你想要的特定做法而言,有一个问题。在你提供的代码中,WebDefaultException<T>是没有意义的,因为你没有提供T。不过,你可以通过如下方式(有些笨拙)避免这个问题(另一个单元测试通过):

    [TestMethod]
    public void CallingGenericMethod()
    {
        Assert.IsTrue(GenericExceptionMethod<int>());
    }

    private bool GenericExceptionMethod<T>()
    {
        bool hitException = false;
        try
        {
            throw new WebFaultException<int>();
        }
        catch (WebFaultException<string> e)
        {
            hitException = false;
        }
        catch (WebFaultException<T> e)
        {
            hitException = true;
        }
        return hitException;
    }

也就是说,如果你处理异常的方法(或类)具有泛型参数,则实际上可以捕获WebFaultException<T>。但是我要提醒一句话,这样做非常奇怪 - 作为您方法的客户端,我被迫传入一个对我来说无关紧要且是您内部实现细节的类型,用于捕获某个异常。

所以,我会说是可以的。但最好还是尽量避免使用,因为这样做可能会让人感到尴尬或者不明智。


我本以为可能是这样,但我实际上并没有在C#中做任何网络相关的工作,编写一个比查找更快:) 我更感兴趣的是处理通用异常的概念。 - Erik Dietrich

3

很遗憾这是不可能的。您可以使用WebFaultException的通用非泛型基类,然后在处理程序中捕获并处理或重新抛出更具体的异常。

我建议不要依赖于异常处理中的泛型类型参数。每当您需要根据类型做出决策时,泛型都会成为障碍。


2

还有几点尚未提到:

  1. 如果可以捕获接口,那么就可以使用“Catch IFooException”语句来捕获“IFooException”,但由于只能捕获继承自Exception的类类型,而类类型不支持协变性,因此在使用通用异常的继承概念时存在一定的限制。最接近的方法是让“FooException”从“FooException”派生。这可能不是一个坏主意,但会有点笨重。
  2. 如果捕获了“SomeException Ex”并将其传递给工厂方法“MakeGenericException(T Ex) where T:Exception”,该方法应生成“FooException”的实例,则即使捕获的异常实际上是“SomeException”的派生类,生成的异常也始终是“FooException”类型。鉴于缺乏协变的通用异常类型,这可能是件好事(因为代码无法捕获预期捕获“FooException”的代码),但仍值得注意。

使用通用类型参数处理异常似乎是一种很好的能力,特别是考虑到一组构造函数可以用于整个异常族。不幸的是,它似乎没有想象中那么有效。


1

您可以通过从一个共同的基类派生您的异常类来管理类似的情况

public class WebFaultException<T> : WebFaultException
{
    public WebFaultException(T content) : base(content)
    public T Content { get { return ObjContent; } }
}

public class WebFaultException
{
    public WebFaultException(object content)
    {
        ObjContent = content;
    }

    public object ObjContent { get; private set; }
}

现在您可以捕获WebFaultExceptionWebFaultException<string>

1

泛型是一种独立的类型,运行时没有模板参数的概念,因此WebFaultException与WebFaultException无关。

如果我是你,我会创建一个非泛型的WebFaultException基类,并从中派生出你的泛型类型。


0
使用非泛型基类。通常情况下,拥有一个非泛型基类是很好的实践方式。您还可以将所有非泛型数据和功能推到它中,从而解决具有不同泛型类型列表的问题。
与其这样做(无论是否继承自Exception):
sealed class MyClass<T> {
   public T GenericProperty { get; set; }
   public int NonGenericProperty { get; set; }
}

做这个:
// optionally abstract
class MyClass {
   public int NonGenericProperty { get; set; } 
}

sealed class MyClass<T> : MyClass {
   public T GenericProperty { get; set; }
}

0

我个人将异常的创建抽象为一个派生自Exception的通用类。

https://net7mma.codeplex.com/SourceControl/latest#Common/Exception.cs

我还定义了一个接口IExceptionEx来容纳派生。

这使您有机会像这样包装异常...

try{ /*容易出错的逻辑*/ } catch (Exception ex) { Common.ExceptionExtensions.CreateAndRaiseException(this, "无法从给定位置解析主机。请参见InnerException。", ex); }

然后像这样捕获特定类型...

catch (Common.Exception<RtspClient>) { throw; } catch (Common.Exception<SessionDescription>) { Common.ExceptionExtensions.CreateAndRaiseException(this, "无法描述媒体,发生会话描述异常。"); }

您可以使用此方法捕获所有派生:

catch (Common.Exception common) {}

或者使用此方法捕获每个异常

catch (Exception ex) {}


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接