开发者

When to use collections vs multiple properties

I'm building a relatively simple class (ex: Person) for an ASP.NET application that has several related boolean properties (ex: Certifications). I have the option of storing them as multiple properties:

Class Person
    Property HasCertL1
    Property HasCertL2
    Property HasCertL3
End Class

Or using a collection and enum:

Enum Cert
    L1
    L2
    L3
End Enum

Class Person
    Property Certs as List(Of Cert)
End Class

The class will not be used elsewhere, and the items (Certs) will be not be updated without recompiling the class anyway. I'm looking for reasons to choose one over the other. Can anyone offer any best practices or point me to some resour开发者_如何学JAVAces that cover this that I may have missed? Thanks in advance!

Cheers, JE


My vote is to use a collection. Here's why: when you set the class up with a property for each level of certification, if you were to add a level of certification, you'd have to add extra code to check that people had that extra level. That requires, possibly, a change to every other class that uses Person.

If you use a list, the Person class doesn't change; there's just a change in the available values for the list. That requires far fewer code changes to check for a particular level of certification.


I think it's always a good approach to keep an eye on extendability of your applications. With that in mind I would go for the collection approach. However it's not clear from your question if there are just three certifications or if there are more.

You're talking about only updating certifications with recompiling in that case it sounds that there are options for extension.


My opinion is that it absolutely depends on how are you going to work with those properties. For any use case usually you can say whether it is more convenient to work with an object as a collection or as a set of several properties. This should drive your choice though it's not that difficult to convert several properties into collection or split collection back into items.

Your situation (you know that you have Cert1, Cert2 and Cert3 and nothing else matters) is pretty rare comparing to cases where nobody knows how many items might a collection have, so most of the times having limited number of properties is not an option. In your case it might be an option. Separate properties may allow you to store objects in more strongly typed way.

If you do not know what option to choose I would advice to choose both. Implement a Person class which will have both collection property and separate property for each item. Internal implementation doesn't matter, only interface matters here. Then you will be able to use anything from other classes and usages probably will make it more clear how it should be implemented. Sample implementation (it doesn't fully implement collection part since you cannot add item to it):

class Person
{
    public Cert1 Cert1 { get; set; }
    public Cert2 Cert2 { get; set; }
    public Cert3 Cert3 { get; set; }

    public IEnumerable<Cert> AllCertificates
    {
        get
        {
            return new Cert[] {Cert1, Cert2, Cert3}
                        .Where(c => c != null)
                        .ToArray();
        }
    }
}


Why do you need a "collection and an enum" ? What's the problem with using a Bit Field enum with the "Flags" attribute to store what "Certs" a "Person" has? This is really just a much cleaner version of option 1.

Per your comment, here is a very trivial example of what I'm talking about:


class Person
{
    public string Name;
    public Certifications Certifications;
}

[Flags]
enum Certifications : int
{
    A = 1,
    B = 2,
    C = 4,
    D = 8,
    E = 16,
    F = 32,
    G = 64,
    H = 128,
    I = 256
};

static void Main()
{
    Person a = new Person() { Name = "rob", Certifications =  Certifications.A | Certifications.D | Certifications.G };
    Person b = new Person() { Name = "jeb", Certifications = Certifications.B | Certifications.C | Certifications.I };

    // Easy way using [Flags] ToString() override.
    DisplayPerson(a);
    DisplayPerson(b);

    Console.WriteLine();

    // Traditional Way
    DisplayPersonInfo( a );
    DisplayPersonInfo( b );
}

static IEnumerable<string> GetCerts( Person person )
{
    foreach( Certifications cert in Enum.GetValues( typeof( Certifications ) ) )
    {
        if( PersonHasCert( person, cert ) )
            yield return ( Enum.GetName( typeof( Certifications ), cert ) );
    }
}

static bool PersonHasCert( Person person, Certifications cert )
{
    return ( ( cert & person.Certifications ) == cert );
}

static void DisplayPersonInfo( Person p )
{
    Console.WriteLine
    (
        String.Format
        (
            "Name: {0}, Certifications: {1}", 
            p.Name, 
            String.Join( ", ", GetCerts( p ) )
        )   
    );
}

static void DisplayPerson(Person p)
{
    Console.WriteLine
    (
        String.Format
        (
            "Name: {0}, Certifications: {1}",
            p.Name,
            p.Certifications
        )
    );
}

Output:

Name: rob, Certifications: A, D, G
Name: jeb, Certifications: B, C, I

Name: rob, Certifications: A, D, G
Name: jeb, Certifications: B, C, I

The aforementioned "[Flags]" attribute helps the compiler know what you're using this data structure for and displaying the values it contains (using the ToString() method on an "Enum" with the attribute, so you don't have to iterate the Enum and do a bit-wise test for each value when you want to display all the values).

What are you other reservations on using this approach? It seems to fit exactly what you're wanting to achieve very efficiently and cleanly.

this is a pretty good article covering the subject.

Edit 2:

Sample code updated to include full example program using both ways to achieve this functionality and some helper methods that you should be able to utilize in your application.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜