何时使用集合vs多个属性

时间:2021-12-12 12:46:49

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:

我正在为ASP构建一个相对简单的类(例如:Person)。具有若干相关布尔属性(例如:认证)的NET应用程序。我可以选择将它们存储为多个属性:

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 resources that cover this that I may have missed? Thanks in advance!

这个类将不会在其他地方使用,而且如果不重新编译这个类,这些项(cert)也不会被更新。我在寻找选择其中之一的原因。有没有人能提供一些最佳实践,或者给我一些我可能错过的参考资料?提前谢谢!

Cheers, JE

干杯,我

4 个解决方案

#1


3  

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.

我的投票是使用集合。原因如下:当您为每个级别的认证设置了一个属性时,如果您要添加一个级别的认证,您必须添加额外的代码来检查人们是否拥有这个额外的级别。这可能需要对使用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.

如果使用列表,Person类不会改变;列表的可用值有一个变化。这需要更少的代码更改来检查特定级别的认证。

#2


2  

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.

您说的只是通过重新编译来更新证书,在这种情况下,似乎有扩展选项。

#3


1  

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.

与没有人知道集合可能有多少项的情况相比,您的情况(您知道您有Cert1、Cert2和Cert3,其他什么都不重要)是非常罕见的,因此大多数情况下,具有有限数量的属性不是一个选项。在你的情况下,这可能是一个选择。单独的属性可以允许您以更强类型的方式存储对象。

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):

如果您不知道选择什么选项,我将建议您选择两者。实现一个Person类,该类将为每个项具有集合属性和独立属性。内部实现不重要,这里只涉及接口。然后,您将能够使用来自其他类的任何内容,并且可能会更清楚地说明应该如何实现它。示例实现(它没有完全实现集合部分,因为不能向其中添加项):

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

#4


1  

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.

为什么需要“集合和枚举”?使用带有“Flags”属性的位域enum来存储“Person”具有的“Certs”有什么问题?这只是选项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

姓名:rob, certificate: A, D, G Name: jeb, certificate: B, C, I

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

姓名:rob, certificate: A, D, G Name: jeb, certificate: 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).

前面提到的“旗帜”属性帮助编译器知道你在使用这个数据结构和显示它所包含的值(使用ToString()方法的“枚举”属性,所以你不需要遍历枚举和为每个值做位操作测试当你想显示所有的值)。

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:

编辑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.

示例代码更新为包含完整的示例程序,使用这两种方法来实现此功能,以及您应该能够在应用程序中使用的一些辅助方法。

#1


3  

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.

我的投票是使用集合。原因如下:当您为每个级别的认证设置了一个属性时,如果您要添加一个级别的认证,您必须添加额外的代码来检查人们是否拥有这个额外的级别。这可能需要对使用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.

如果使用列表,Person类不会改变;列表的可用值有一个变化。这需要更少的代码更改来检查特定级别的认证。

#2


2  

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.

您说的只是通过重新编译来更新证书,在这种情况下,似乎有扩展选项。

#3


1  

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.

与没有人知道集合可能有多少项的情况相比,您的情况(您知道您有Cert1、Cert2和Cert3,其他什么都不重要)是非常罕见的,因此大多数情况下,具有有限数量的属性不是一个选项。在你的情况下,这可能是一个选择。单独的属性可以允许您以更强类型的方式存储对象。

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):

如果您不知道选择什么选项,我将建议您选择两者。实现一个Person类,该类将为每个项具有集合属性和独立属性。内部实现不重要,这里只涉及接口。然后,您将能够使用来自其他类的任何内容,并且可能会更清楚地说明应该如何实现它。示例实现(它没有完全实现集合部分,因为不能向其中添加项):

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

#4


1  

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.

为什么需要“集合和枚举”?使用带有“Flags”属性的位域enum来存储“Person”具有的“Certs”有什么问题?这只是选项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

姓名:rob, certificate: A, D, G Name: jeb, certificate: B, C, I

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

姓名:rob, certificate: A, D, G Name: jeb, certificate: 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).

前面提到的“旗帜”属性帮助编译器知道你在使用这个数据结构和显示它所包含的值(使用ToString()方法的“枚举”属性,所以你不需要遍历枚举和为每个值做位操作测试当你想显示所有的值)。

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:

编辑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.

示例代码更新为包含完整的示例程序,使用这两种方法来实现此功能,以及您应该能够在应用程序中使用的一些辅助方法。