3

I've currently got a class that has multiple string properties. One or more of them may have a value. What I need to do is get the first non-empty property, based off some priority. Here is an example of what I envision the class would look like:

public class MyClass
{
    [Priority(1)]
    public string HighestPriority { get; set; }

    [Priority(2)]
    public string MiddlePriority { get; set; }

    [Priority(3)]
    public string LowestPriority { get; set; }
}

Or, maybe use another enum property that can be used to determine the highest one that is set?

public enum Priority
{
    HighestPriority,
    MiddlePriority,
    LowestPriority
}

public class MyClass
{
    private string highestPriority;
    public string HighestPriority 
    { 
        get; 
        set
        {
            highestPriority = value;
            HighestSetProperty = Priority.HighestPriority;
        }
    }

    private string middlePriority;
    public string MiddlePriority
    { 
        get; 
        set
        {
            middlePriority = value;
            if (HighestSetProperty != Priority.HighestPriority)
                HighestSetProperty = Priority.MiddlePriority;
        }
    }

    private string lowestPriority;
    public string LowestPriority
    { 
        get; 
        set
        {
            lowestPriority = value;
            if (HighestSetProperty != Priority.HighestPriority || HighestSetProperty != Priority.MiddlePriority)
                HighestSetProperty = Priority.LowestPriority;
        }
    }

    public Priority HighestSetProperty { get; set; }
}

Then, use HighestSetProperty to set the string in a switch statement?

So far though, the only way I know of to find the first non-empty property, without using some form of priority attribute like above, is something like this:

string highestPriorityProp = String.IsNullOrWhitespace(HighestPriority) ? (String.IsNullOrWhitespace(MiddlePriority) ? LowestPriority : MiddlePriority) : HighestPriority;

Which is obviously messy and doesn't scale well if more properties are added. So, what is the best way (in terms of readability, speed and scalability) of doing this? Thanks in advance.

EDIT: Let's go for cleanliness and scalability over speed.

EDIT: The duplicate question doesn't actually answer my question. As the properties may not be in the order of priority. Hence, the first one that is looped through may be a lower priority than the highest one set. For instance:

public class MyClass
{
    public string MiddlePriority { get; set; }

    public string HighPriority { get; set; }
}

EDIT: Thanks for the help everyone. As mjwills and I have discussed in the comments, The accepted answer would suit my needs as I have only around 6 properties in my class. But, if there were more, the duplicate answer is probably the best way to go.

AzaDee
  • 85
  • 7
  • Maybe you can use a priority dictionary where keys are the priorities. Then just get the first not null value. Linq should make it straightforward. Or Just have a PriorityString class having the value and priority as the members and again use linq to sort and get the first not null value. – peeyush singh Feb 13 '19 at 02:14
  • It depends if you want speed, or cleanliness, as they will give you a different solution. if you want to worry about changing something when you add or change properties, use reflection, if you dont mind updating some code when you add a property, use a compiled code options like you are doing or create an enumerable or list to map to – TheGeneral Feb 13 '19 at 02:16
  • 1
    Possible duplicate of [Reflection - get attribute name and value on property](https://stackoverflow.com/questions/6637679/reflection-get-attribute-name-and-value-on-property) - then `OrderBy` the `Priority`. – mjwills Feb 13 '19 at 02:24
  • @mjwills thanks. But, that is assuming I use the priority attribute or enum method. I am asking what method will give me the most maintainable code. As I am dealing with about 6 properties. Do I use the enum method? Attribute method? Another Method (such as John Wu has pointed out). etc. – AzaDee Feb 13 '19 at 02:43
  • Ultimately you need to encode the order **somewhere** (either in code, like the answer below, or priority attributes etc). My suggestion was based on your sample code. Alas, if you are asking for 'the most maintainable' answer, then you are veering dangerously close to 'opinion based' (i.e there is no hard and fast definition of what that means - see Michael Randall's comment above) and the question may be closed. – mjwills Feb 13 '19 at 02:45
  • 1
    Honestly, if you have only 6 properties I'd use John Wu's approach. If you had 50, I'd use the duplicate link. – mjwills Feb 13 '19 at 02:47
  • Sorry, "maintainable" is probably the wrong word to use here. I am imagining a scenario where you have a class with a large amount of properties and you are trying to get the highest priority value from a large number of these objects many times a second. The for loop referenced could be slower than John Wu's answer. But, easier to read/scale than his. So, there would be an empirical difference between the two. Hence, what way gives the fastest speed vs which scales best? – AzaDee Feb 13 '19 at 02:59

2 Answers2

4

Normally you can do something like this with the coalesce operator:

var highest = high ?? medium ?? low;

But it seems you want to treat "null" and "empty string" the same. So write a function that converts empty strings to nulls:

Func<string,string> func = s => s == "" ? null : s;

Then coalesce over that:

var highest = func(HighestPriority) ?? func(MediumPriority) ?? func(LowPriority);

I recommend this over using attributes and reflection. Yes you'll have to modify the code when you add a new property, but you're already modifying the code to add a property itself-- there is little point is making only half the solution automatic. While using a "flexible" solution and Reflection is a common temptation, in my experience it is usually counterproductive in the long term.

If you are married to the idea of scaling and adsorbing more and more properties automatically, I suggest using a List or Dictionary and using something like First().

John Wu
  • 50,556
  • 8
  • 44
  • 80
  • Thanks. I am actually trying to create a display name for an asp.net view. So, if the high is null or "", use the next one. For instance, if the properties were FirstName and LastName, DisplayName = FirstName ?? LastName. But, there are about 6 properties that I am working with in total. – AzaDee Feb 13 '19 at 02:30
0

I would go with John Wus answer and use coalesce operator, but there are other possiblities to get yourself a null instead of an empty string:

Create a extensionmethods class and extend the 'string' class:

            public static class ExtensionMethod
            {

                    public static string ConvertEmptyToNull(this string str)
                    {
                            return string.IsNullOrEmpty(str) ? null : str;
                    }

            }

Than use coalesce operator:

    var highest = HighestPriority.ConvertEmptyToNull() ?? MediumPriority.ConvertEmptyToNull() ?? LowPriority.ConvertEmptyToNull();

But in your case i would rather implement the getter of your properties since you have private fields for your properties.

            private string highestPriority;
            public string HighestPriority
            {
                    get
                    {
                            return string.IsNullOrEmpty(highestPriority) ? null : highestPriority;
                    }
                    set
                    {
                            highestPriority = value;
                            HighestSetProperty = Priority.HighestPriority;
                    }
            }

Now your coalesce chain will look cleaner:

    var highest = HighestPriority ?? MediumPriority ?? LowPriority;
VRage
  • 1,458
  • 1
  • 15
  • 27