有没有办法在调用基类构造函数时使用可覆盖的行为?

时间:2008-11-27 12:49:02

标签: c# .net

标题有点抽象,所以可能更容易用一个具体的例子来解释:

我发现让异常类采用枚举参数而不是字符串消息很有用。

throw new SpecificException(SpecificExceptionCode.ThisThingWentWrong);

这样做的原因很少,包括:

  • 我可以在一个地方包含所有访问本地化字符串资源的逻辑
  • 我可以轻松确保我的所有异常消息都有字符串资源
  • 在单元测试中检查正确的异常消息更简单

我想为这种类型的异常编写一个基类。派生的异常类通常只想提供自己的System.Resources.ResourceManager,但也可能提供其他构造函数。麻烦来了,因为我只能在调用基类构造函数时调用静态方法。这导致我有类似的事情:

public abstract class BaseException : ApplicationException
{
    protected static ResourceManager m_resources;
    public BaseException(System.Enum errorCode, params object[] messageArgs) 
        : base(ProcessError(errorCode, messageArgs))
    {}

    private static string ProcessError(Enum errorCode, params object[] messageArgs)
    {
        string errorMessage = m_resources.GetString(errorCode.ToString());
        // Handling of messageArgs and trace logging
        // ....
        return finalError;
    }
}

public class SpecificException : BaseException
{
    static SpecificException()
    {
        m_resources = //.. Get an appropriate resource manager instance
    }

    public SpecificException(SpecificExceptionCode errorCode, params object[] messageArgs)
        : base(errorCode, messageArgs)
    {}
}

这很有效,但我对它不满意,因为没有编译时暗示派生类必须提供自己的System.ResourceManager。我想有一个基类,如:

public abstract class BaseException : ApplicationException
{
    protected abstract static ResourceManager Resources{get;}

    public BaseException(System.Enum errorCode, params object[] messageArgs) 
        : base(ProcessError(errorCode, messageArgs))
    {}

    private static string ProcessError(Enum errorCode, params object[] messageArgs)
    {
        string errorMessage = Resources.GetString(errorCode.ToString());
        // Handling of messageArgs and trace logging
        // ....
        return finalError;
    }
}

...但我不能有一个抽象的静态方法。还有更好的方法吗?

3 个答案:

答案 0 :(得分:2)

您当前的代码已损坏...只有一个静态字段;执行胜利的最后一个静态ctor。

重新使用非静态方法 - 请注意,在构造函数中调用虚拟/抽象方法有点危险 - 具体类尚未初始化,因此覆盖可能会尝试使用尚不可用的数据

ResourceManager不能是基础构造函数的参数吗?如果它们传递null,则使用合理的默认值... 然后ProcessError将接受ResourceManager等

public abstract class BaseException : ApplicationException
{
    static ResourceManager defaultManager;
    static ResourceManager DefaultManager
    {
        get
        {
            if (defaultManager == null) defaultManager = TODO; // sensible default
            return defaultManager;
        }
    }

    public BaseException(System.Enum errorCode, params object[] messageArgs)
        : this(DefaultManager, errorCode, messageArgs) {}
    public BaseException(ResourceManager resourceManager, System.Enum errorCode, params object[] messageArgs)
        : base(ProcessError(resourceManager, errorCode, messageArgs))
    { }

    private static string ProcessError(ResourceManager resourceManager, Enum errorCode, params object[] messageArgs)
    {
        if (resourceManager == null) throw new ArgumentNullException("resourceManager");
        string errorMessage = resourceManager.GetString(errorCode.ToString());
        // Handling of messageArgs and trace logging
        // ....
        return finalString;
    }
}

public class SpecificException : BaseException
{
    static ResourceManager customManager;
    static SpecificException()
    {
        customManager = //TODO - init manager
    }
    public SpecificException(SomeEnum errorCode, params object[] messageArgs)
        : base(customManager, errorCode, messageArgs)
    { }
}

不想提供自己的经理的类只使用另一个ctor:

: base(errorCode, messageArgs)

答案 1 :(得分:0)

为什么它必须是静态属性?您可以使资源正常属性:

protected abstract static ResourceManager Resources{get;}

然后让实现者实现它以返回一个静态对象:

private static ResourceManager resources = ....

protected override static ResourceManager Resources{get {return resources; }}

答案 2 :(得分:0)

为什么不使用受资源管理器作为参数的受保护构造函数?这样你的具体例外就必须传入它。

我倾向于使用工厂方法而不是构造函数来解决这类问题。

public static SpecificException SpecificExceptionCodeName()
{
    //Do resource lookup and create Exception
}

然后使用

throw ExceptionBuilder.SpecificExceptionCodeName();

通过这种方式,您可以获得所概述的好处,而无需继承特定的基本异常,从而允许您抛出任何您喜欢的异常。框架中有几个地方在内部使用此方法。请注意,我倾向于不将工厂方法放在具体的异常上,因为它们会出现在可以得到的派生类上。

相关问题