我想创建一个分层枚举,表示我可以作为参数传递的类型。
数据结构如下所示:
enum Cars
{
Ford { Corsair, Cortina, Galaxy, GT },
Ferrari { Testarossa, California, Enzo },
...
}
我希望使用以下签名调用函数:
public void BuildCar(Cars car);
像这样:
BuildCar(Cars.Ferrari.Enzo);
基本上,我想在类型中强制执行汽车/制造商关系。
答案 0 :(得分: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);
答案 1 :(得分:5)
自己动手解决方案:
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
课程中包含的所有功能。但这是可以理解的,因为您需要通常不支持的逻辑。
答案 2 :(得分:3)
您可以使用枚举来定义所有汽车,然后在每个制造商的类上使用静态成员来返回允许的值:
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;
当然,这对语法糖的好处可能不值得让实际的枚举与常数类成员保持同步。
除此之外,使用字符串常量可能是您的最佳选择。枚举不能嵌套或继承,并且它们不能共享允许在不同名称空间或类中指定相同Enum类型的值的公共接口。
答案 3 :(得分:2)
是的,你可以得到这个:
BuildCar(Cars.Ferrari.Enzo)
但你不会得到这个:
enum Cars
{
Ford { Corsair, Cortina, Galaxy, GT },
Ferrari { Testarossa, California, Enzo },
...
}
你可以做的是一个带有私有构造函数的密封类,它以一种树结构公开intances,你需要一些帮助类。
以下代码有效:
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。
First Enum2D.ttinclude:
<#@ 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文件中维护并不会太痛苦。
干杯!
答案 4 :(得分:0)
您可以创建Cars类,其中包含每个make的嵌套类,其中包含每个模型的consts