16

我想创建一个分层枚举,它代表我可以作为参数传递的类型。

数据结构如下所示:

enum Cars
{
   Ford { Corsair, Cortina, Galaxy, GT },
   Ferrari { Testarossa, California, Enzo },
   ...
}

我希望调用具有以下签名的函数:

public void BuildCar(Cars car);

像这样:

BuildCar(Cars.Ferrari.Enzo);

基本上,我想在类型中强制执行汽车/制造商关系。

4

5 回答 5

6

我想过用扩展和/或ICustomEnum东西来回答这个问题。

然后我想-以下内容真的有那么糟糕吗?:-)

    enum Cars
    {
       Ford_Corsair,
       Ford_Cortina,
       Ford_Galaxy,
       Ford_GT,

       Ferrari_Testarossa,
       Ferrari_California,
       Ferrari_Enzo,
    }

您的功能仍然如下所示:

public void BuildCar(Cars car);

你的电话看起来像:

BuildCar(Cars.Ferrari_Enzo);
于 2012-08-20T06:21:34.513 回答
6

自己动手解决方案:

class Cars
{
    private static int CurrentId = 0;

    private readonly int id;

    private Cars()
    {
        id = CurrentId;
        CurrentId++;
    }

    public static class Ford
    {
        public static Cars Corsair = new Cars();
        public static Cars Cortina = new Cars();
        public static Cars Galaxy = new Cars();
        public static Cars GT = new Cars();
    }

    public static class Ferrari
    {
        public static Cars Testarossa = new Cars();
        public static Cars California = new Cars();
        public static Cars Enzo = new Cars();
    }

    // Add overrides of Equals and GetHash from id
}

这样,您将丢失Enums类中包含的所有功能。但这是可以理解的,因为您需要通常不支持的逻辑。

于 2012-08-20T04:46:04.183 回答
4

是的,你可以得到这个:

BuildCar(Cars.Ferrari.Enzo)

但你不会得到这个:

enum Cars
{
   Ford { Corsair, Cortina, Galaxy, GT },
   Ferrari { Testarossa, California, Enzo },
   ...
}

你可以做的是一个带有私有构造函数的密封类,它以一种树结构公开实例,你需要一些帮助类。

以下代码有效:

using System;

namespace _2DEnum
{
    public class Example
    {
        public void BuildCar(Cars car)
        {
            GC.KeepAlive(car);
        }

        public void ExampleUse()
        {
            BuildCar(Cars.Ferrari.Enzo);
        }
    }

    public sealed class Cars
    {
        private readonly string _value;

        private Cars(string value)
        {
            if (ReferenceEquals(value, null))
            {
                    throw new ArgumentNullException("value");
            }
            else
            {
                _value = value;
            }
        }

        public override string ToString()
        {
            return _value;
        }

        public static class Ferrari
        {
            private static readonly Cars _California = new Cars("California");
            private static readonly Cars _Enzo = new Cars("Enzo");
            private static readonly Cars _Testarossa = new Cars("Testarossa");

            public static Cars California
            {
                get { return Ferrari._California; }
            }

            public static Cars Enzo
            {
                get { return Ferrari._Enzo; }
            }

            public static Cars Testarossa
            {
                get { return Ferrari._Testarossa; }
            }
        }

        public static class Ford
        {
            private static readonly Cars _Corsair = new Cars("Corsair");
            private static readonly Cars _Cortina = new Cars("Cortina");
            private static readonly Cars _Galaxy = new Cars("Galaxy");
            private static readonly Cars _GT = new Cars("GT");

            public static Cars Corsair
            {
                get { return Ford._Corsair; }
            }

            public static Cars Cortina
            {
                get { return Ford._Cortina; }
            }

            public static Cars Galaxy
            {
                get { return Ford._Galaxy; }
            }

            public static Cars GT
            {
                get { return Ford._GT; }
            }
        }
    }
}

再说一次,您可以尝试使用一些文本转换模板来像枚举一样键入它。

感谢@ Euphoric ...我需要一个过度分析徽章。


文本转换模板!

我为 T4 创建了一个解决方案,该解决方案使用文件 Enum2D.ttinclude 包含生成此 2D 枚举的代码和每个“2D 枚举”的文件,例如 Cars.tt。

第一个 Enum2D.tt 包括:

<#@ import namespace="System" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #><#+
    public string BuildEnum2D
    (
        string namespaceName,
        string enumName,
        IEnumerable<Tuple<string, IEnumerable<string>>> items
    )
    {
        string head =
@"
using System;

namespace @namespace
{
    public sealed class @enum
    {
        private readonly string _value;

        private @enum(string value)
        {
            if (ReferenceEquals(value, null))
            {
                throw new ArgumentNullException(""value"");
            }
            else
            {
                _value = value;
            }
        }

        public override string ToString()
        {
            return _value;
        }";
        head = head.Replace("@namespace", namespaceName);
        head = head.Replace("@enum", enumName);
        StringBuilder sb = new StringBuilder();
        sb.Append(head);
        foreach (var item in items)
        {
            sb.Append(
@"

        public static class " + item.Item1 + 
@"
        {"
                    );
            foreach (var entry in item.Item2)
            {
                sb.Append(
@"
            private static readonly Cars _" + entry + @" = new Cars(""" + entry + 
@""");"
                        );
            }

            foreach (var entry in item.Item2)
            {
                sb.Append(
@"

            public static Cars " + entry + @"
            {
                get { return _" + entry + @"; }
            }"
                        );
            }
            sb.Append(
@"
        }"          );

        }
        sb.Append(
@"
    }
}"
                );
        return "// <auto-generated />" + "\r\n" + sb.ToString();
    }
#>

如您所见,该文件定义了一个函数BuildEnum2D,该函数的唯一目的是在给定参数的情况下为您的 2D 枚举生成代码。现在我们只需要调用它。为此,我们将使用 Cars.tt,如下所示:

<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ include file="Enum2D.ttinclude" #>
<#@ output extension=".cs" #><#=
    BuildEnum2D
    (
        "Enum2DTest",
        "Cars",
        new Tuple<string, IEnumerable<string>>[]
        {
            new Tuple<string, IEnumerable<string>>
            (
                "Ferrari",
                new string[]
                {
                    "California",
                    "Enzo",
                    "Testarossa"
                }
            ),
            new Tuple<string, IEnumerable<string>>
            (
                "Ford",
                new string[]
                {
                    "Corsair",
                    "Cortina",
                    "Galaxy",
                    "GT"
                }
            )
        }
    )
#>

如您所见,我们包含文件 Enum2D.ttinclude 然后调用函数 Build2DEnum,参数是命名空间、枚举名称和项目。这样,您将能够创建更多这些 2D 枚举,并希望在 tt 文件中维护不会太痛苦。

干杯!

于 2012-08-20T04:43:50.647 回答
3

You could use an Enum to define all cars, then use static members on a class for each Manufacturer to return the allowed values:

enum Car {
  FordCorsair,
  FordCortina,
  FordGalaxy,
  FordGT,
  FerrariTestarossa,
  FerrariCalifornia,
  FerrariEnzo,
  ...
}

public sealed class Ford {
  public const Car Corsair = Car.FordCorsair;
  public const Car Cortina = Car.FordCortina;
  public const Car Galaxy = Car.FordGalaxy;
  public const Car GT = Car.GT;
}

public sealed class Ferrari {
  public const Car Testarossa = Car.FerrariTestarossa;
  public const Car California = Car.FerrariCalifornia;
  public const Car Enzo = Car.FerrariEnzo;
  public const Car GT = Car.GT;
}

...
Car mycar = Ford.Corsair;

Of course, the syntactic sugar benefits of this may not be worth the pain of keeping the actual enum synchronized with the constant class members.

Aside from the above, using string constants is likely your best option. Enums can't be nested or inherited, and they can't share a common interface that would allow values for the same Enum type to be specified in different namespaces or classes.

于 2012-08-20T05:25:15.257 回答
0

您可以创建 Cars 类,该类将包含每个 make 的嵌套类,该类将包含每个模型的 const

于 2012-08-20T04:48:30.687 回答