当使用NotSupportedException不好时?

4
我正在建立一个本地化目录并遇到了设计问题。目前,目录使用 Dictionary<string, IString> 存储翻译,其中 IString 可以是两种类型之一: SingularPlural。这是 IString 的简化版本:
public interface IString
{
    void SetSingular(string singular);

    string GetSingular(params object[] args);

    void SetPlural(PluralCategory category, string plural);

    string GetPlural(PluralCategory category, params object[] args);
}

接下来,当我实现Singular时,对于复数方法我会抛出一个NotSupportedException的异常,这个异常被目录所捕获,Plural也会对单数方法执行同样的操作。

public class Singular : IString
{
    // ...

    public string GetPlural(PluralCategory category, params object[] args)
    {
        throw new NotSupportedException(string.Format(
            "Singular strings don't support GetPlural({0}, {1})",
            category, args));
    }

    public void SetPlural(PluralCategory category, string plural)
    {
        throw new NotSupportedException(string.Format(
            "Singular strings don't support SetPlural({0}, {1})",
            category, plural));
    }
}

这个设计的要求是,键必须对单数和复数都是唯一的,但我认为实现非关联方法并抛出NotSupportedException不好。
第二种设计是为了进行键检查而存储一个Dictionary<string, StringType>,其中StringType是一个枚举类型,并且有两个额外的字典Dictionary<string, Singular>Dictionary<string, Plural>,根据StringType的值选择使用。这使得它稍微复杂了一些,但不会破坏接口的概念。
那么,你怎么想?我的第一个想法使用NotSupportedException是不好的吗?我应该选择第二种设计还是其他什么东西?
编辑:根据@dasblinkenlight的想法,一种更清晰的解决方案是将获取SingularPlural的接口方法统一为单个方法。Singulars只有一个PluralCategory.None,而Plurals绝不允许包含此类别。这由setter方法限制,不在接口中定义,而是由特定实现定义(setter方法不在下面显示)。
public interface IString
{
    string GetString(PluralCategory category, params object[] args);
    bool HasCategory(PluralCategory category);
}

public class Singular : IString
{
    private string Text;

    public string GetString(PluralCategory category, params object[] args)
    {
        if (category == PluralCategory.None)
        {
            if (Text == null || args == null || args.Length == 0)
            {
                return Text;
            }
            return string.Format(Text, args);
        }
        return null;
    }

    public bool HasCategory(PluralCategory category)
    {
        return category == PluralCategory.None;
    }
}

public class Plural : IString
{
    private Dictionary<PluralCategory, string> Texts = new Dictionary<PluralCategory, string>();

    public string GetString(PluralCategory category, params object[] args)
    {
        string text;
        if (Texts.TryGetValue(category, out text))
        {
            if (text == null || args == null || args.Length == 0)
            {
                return text;
            }
            return string.Format(text, args);
        }
        return null;
    }

    public bool HasCategory(PluralCategory category)
    {
        return Texts.ContainsKey(category);
    }
}

嗯,还好吧。InvalidOperationException 对我来说总是更有意义。别给打电话问为什么不支持它。天哪。 - Hans Passant
不要忘记dual - Guillaume
如果你正在使用这个来进行本地化,请立即停止。你已经陷入了很大的麻烦。你有考虑过性别吗? - Stu
@Guillaume 这是基于 CLDR 复数形式的,其中包括双数。 - moraes
啊,没错。这就是想法。这种基于短语的本地化,比如gettext、Android字符串资源或其他许多工具。 - moraes
显示剩余2条评论
1个回答

7
你说得对,像这样在某种情况下抛出NotSupportedException并不是一个好主意。通常,你实现一个接口是为了提供一个共同的操作集合,将提供不同实现的、不同类别的类“统一”起来。
然而,在你的情况下,这些不同类别的类无法“统一”:它们不仅保留了自己的实现,还保留了自己的接口。当你试图以统一的方式使用它们时,代码会抛出异常。与将它们作为object保存相比,共同的接口并没有给你带来太多优势-用户必须知道IString后面的类是什么,才能进行调用,否则他们可能会看到一个NotSupportedException
解决这个问题有几种方法:一种方法是统一单数和复数方法,返回一个比简单的string更复杂的答案:
public enum SingularOrPluralCategory {
    Singular,
    // The rest of the PluralCategory enum values
}
public class StringForm {
    public string Text {get; private set;}
    public SingularOrPluralCategory Category {get; private set;}
}
public interface IString {
    // You may want to omit the setter from the interface, adding it to the class instead
    void SetForm(SingularOrPluralCategory category, string plural);
    StringForm GetForm(SingularOrPluralCategory category, params object[] args);
}

现在,单数实现将返回一个带有“Category”中的“Singular”值的StringForm,而复数实现将返回一个带有多个复数类别之一的StringForm。试图在IString的单数子类上设置复数类别仍然可能会导致错误,但是您可以通过将setter移动到类上来修复这个问题,这样就不可能在IString的单数子类上调用带有类别的setter。

我现在正在使用这个的变体,跳过StringForm并直接返回字符串。 IString有一个单一的方法string GetString(PluralCategory category, params object[] args)Singular实现只需要一个PluralCategory.None,否则返回null。不再出现异常。感谢这个技巧。 - moraes

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