When using NotSupportedException is this bad?

I am creating a localization directory and have a constructive dilemma. Now the directory stores Dictionary<string, IString> for storing translations, where IString can be of two types: Singular or Plural . This is a simplified version of 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); } 

Then, when I implement Singular , I throw a NotSupportedException for multiple methods that are caught by the directory, and Plural does the same for singular methods.

 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)); } } 

This design is based on the requirement that keys must be unique for both singularities and NotSupportedException , but I feel that implementing unrelated methods and throwing a NotSupportedException doesn't smell very good.

The second design will be to store the Dictionary<string, StringType> for checking the key, where StringType is an enumeration, and then use two additional dictionaries Dictionary<string, Singular> and Dictionary<string, Plural> depending on the value of StringType , This makes it a little more complicated, but does not violate the concept of the interface.

So what do you think? Is my first idea a bad use of NotSupportedException ? Should I go for a second design or something else?

Edit: A clearer solution based on the idea of ​​@dasblinkenlight was to combine the interface methods to get Singular or Plural into one. Singulars only have PluralCategory.None , and Plurals can never contain this category. This is limited by configuration methods left outside the interface, which must be defined by specific implementations (configuration methods are not displayed below).

 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); } } 
+4
source share
1 answer

You are right, it is not a good idea to throw a NotSupportedException in a similar situation. Typically, you implement an interface to provide a common set of operations that unites a group of heterogeneous classes that provide different implementations for a set of common operations.

However, in your case, heterogeneous classes cannot be "unified": they save not only their own implementations, but also their own interfaces. When you try to use them evenly, the code throws an exception. The common interface does not give you everything that supports them as an object - users need to know which class is behind the IString before making a call, otherwise they risk seeing a NotSupportedException .

There are several ways to solve this problem: one way is to unify singular and multiple methods, returning a more complex answer than a simple 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); } 

Now, a single implementation will return a StringForm Singular value in Category , while a multiple implementation will return a StringForm with one of several multiple categories. Trying to set up a multiple category in a separate IString subclass IString still be a mistake, but you can fix this by moving setter to a class, making it impossible to call a setter with a category in an exclusive IString subclass.

+7
source

Source: https://habr.com/ru/post/1495239/


All Articles