你能捕到一个例外<t>哪里有T是什么类型?</t>

时间:2012-01-27 06:58:44

标签: c# generics exception-handling

我希望能够捕获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)
{
}

8 个答案:

答案 0 :(得分:14)

正如其他答案所提到的那样;你不能直接指定捕获每个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 :(得分:3)

不幸的是,这是不可能的。您可以使用WebFaultException的公共非泛型基类,然后使用该处理程序中的反射来捕获并处理或重新抛出更具体的异常。

我建议不要在异常处理中依赖泛型类型参数。每当你必须根据类型做出决定时,泛型将永远妨碍你。

答案 2 :(得分:3)

我从来没有想过这样的事情,所以我很好奇,做了一个小小的便笺实现。首先,以下工作:

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>。但是,我要提醒一点,这是一个非常奇怪的事情 - 作为你的方法的客户,我被迫传递一种类型,将用于我不关心的任何内容,并且是一个内部为您想要捕获的一些异常实现细节。

所以,我会说是的,可能。但充其量也很尴尬,也许是不明智的。

答案 3 :(得分:2)

尚未提及的几点:

  1. 如果有人可以捕获接口,那么`Catch IFooException`语句就有可能捕获`IFooException`,但是因为只能捕获从Exception派生的类类型,并且因为类类型不支持协方差,对于通用异常使用继承概念的能力有限。最接近的是`FooException`派生自`FooException`。这可能不是一个坏主意,但会有点笨重。
  2. 如果有人捕获`SomeException Ex`并将`Ex`传递给工厂方法`MakeGenericException(T Ex),其中T:Exception`,它应该产生一个'FooException`实例,产生的异常即使被捕获的异常实际上是`SomeException`的衍生物,它也总是类型为`FooException`。鉴于缺少协变泛型异常类型,这可能是一件好事(因为无法捕获`FooException`是期望捕获`FooException`的代码)但是仍然值得注意。

使用具有异常的泛型类型参数似乎是一种很好的能力,特别是考虑到一组构造函数可以用于整个异常系列。不幸的是,它似乎没有像人们希望的那样好用。

答案 4 :(得分:1)

泛型类型本身就是一个类型,运行时没有模板化参数的概念,因此WebFaultException与WebFaultException无关。

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

答案 5 :(得分: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>

答案 6 :(得分:0)

我个人抽象地创建了从Exception派生的泛型类的异常。

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

我还定义了一个接口IExceptionEx以适应推导。

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

try{ /*error prone logic*/ } catch (Exception ex) { Common.ExceptionExtensions.CreateAndRaiseException(this, "Could not resolve host from the given location. See InnerException.", ex); }

捕捉特定类型然后就像这样......

catch (Common.Exception<RtspClient>) { throw; } catch (Common.Exception<SessionDescription>) { Common.ExceptionExtensions.CreateAndRaiseException(this, "Unable to describe media, Session Description Exception Occured."); }

你可以抓住所有派生的东西:

catch (Common.Exception common) {}

或者每个例外

catch (Exception ex) {}

答案 7 :(得分: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; }
}