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

日曜大工の解決策:

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 に答える
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 に答える
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への称賛...私は過剰分析バッジが必要です。


テキスト変換テンプレート!

この2D列挙型を生成するコードを保持するファイルEnum2D.ttincludeと、Cars.ttなどの各「2D列挙型」のファイルを使用するT4のソリューションを作成しました。

最初の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ファイルを維持するのにそれほど苦労しないことを願っています。

乾杯!

于 2012-08-20T04:43:50.647 に答える
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;

もちろん、これによる糖衣構文の利点は、実際の列挙型を定数クラスのメンバーと同期させるという苦痛に見合う価値がない場合があります。

上記とは別に、文字列定数を使用するのがおそらく最良のオプションです。列挙型をネストしたり継承したりすることはできません。また、同じ列挙型の値を異なる名前空間またはクラスで指定できるようにする共通のインターフェイスを共有することもできません。

于 2012-08-20T05:25:15.257 に答える
0

各モデルの定数を含む各メーカーのネストされたクラスを含むCarsクラスを作成できます

于 2012-08-20T04:48:30.687 に答える