2

C# で次の構造を (疑似コードで) 達成するための適切な方法はありますか?

void Method(MyClass<Attribute1, Attribute2, ...> foo)
{
  // here I am guaranteed that foo has the specified attributes
}

たとえば、メソッドに必要な属性でインスタンス化された のインスタンスのみをメソッドに渡すことができる (それ以外の場合はコンパイルに失敗する)Attribute*列挙値はどこにありますか?MyClass

C++ テンプレートがこれを機能させることがわかっているのでジェネリックを調べてみたので、論理的な出発点のように見えましたが、エレガントに機能させることはできませんでした (インターフェイスを使用してこの方法でパラメーターの型を制約しようとしましたが、私は少なくとも4つの属性を持っているので、非常にかさばり、率直に言って使い物になりませんでした)。

各メソッドの最初に面倒なチェックがたくさん行われるのを避けるために、これを行いたいと思います。私は DirectX 11 グラフィックス開発を行っているので、この「タイプ セーフ」な方法でオブジェクトを渡すのが特に簡単にならない API によって一種の制約を受けています (DirectX では、すべてのリソースに情報を含む大きな「説明」構造がありますリソースができることとできないこと、できることとできないことなどについて..そして解析するのは面倒でエラーが発生しやすいので、私と私のユーザーの便宜のためにそれの周りにラッパーを書こうとしています)。

また、多くの組み合わせがあるため、ケースごとに異なるクラス タイプを使用することはできません。このようなコードを記述するには、これが最も快適な方法のように思われます。C# でこれが可能になることを願っています。


この種の言語機能には名前があると確信しています (知っている場合は教えてください。Google で調べたはずですが、適切なキーワードがわからない場合、これを検索するのは難しいです...)

4

2 に答える 2

2

.NET のジェネリック型パラメーターは、それ自体が型でなければなりません。ジェネリック型パラメーターの特定の値のみに固有のジェネリック型/メソッドを作成することはできません。

メソッドを制限したい属性値を表す型を作成したくない、または作成できない場合は、メソッドでサニティ チェックを実行して、提供された「foo」オブジェクトで適切な属性値が使用されていることを確認する必要があります。


特定の属性値の表現として特定の型を使用することは、あなたが尋ねた問題に対する答えかもしれませんが、switch-case ステートメントをサポートしないという欠点があります (以下を参照)。私の回答の最後にある最後のメモも読んでください。

たとえば、テクスチャを表す型が必要だとします。テクスチャは、さまざまな数のチャネルとさまざまなビット深度を持つことができます。次に、次のような一般的なテクスチャ タイプを宣言できます。

class Texture<TChannels, TBPC>
    where TChannels : INumOfChannels,new()
    where TBPC : IBitsPerChannel,new()

INumOfChannelsIBitsPerChannelは単なるインターフェイスであり、空にすることができます。new()制約は、インターフェイス自体を使用して具体的な Texture タイプを作成することを防ぎます

さまざまなチャネルとさまざまな BPC に対して、それぞれの基本インターフェイスから拡張された空の型を作成します。次に例を示します。

class FourChannels : INumOfChannels {};
class ThreeChannels : INumOfChannels {};

class BitsPerChannel_24 : IBitsPerChannel {};
class BitsPerChannel_32 : IBitsPerChannel {};

これを使用すると、ジェネリック メソッドを特定の属性の組み合わせに制限できます。メソッドが 4 チャンネルおよび 32bpc テクスチャのみを処理する必要がある場合:

void MyMethod<TChannels, TBPC>(Texture<TChannels, TBPC> foo)
    where TChannels : FourChannels
    where TBPC : BitsPerChannel_32


さて、すべての良いものには暗い面もあります。このようなことをどのように行いますか (疑似コードとして記述)?

switch (NumOfChannelsAttribute)
{
    case FourChannels:
        // do something
        break;

    case ThreeChannels:
        // do something else
        break;
}

「FourChannel」と「ThreeChannel」は型であり、整数値ではないため、少なくとも簡単で単純な方法ではできません。

もちろん、if構文は引き続き使用できます。これが機能するためには、使用される属性を提供するジェネリック テクスチャ タイプにプロパティを実装する必要があります。

class Texture<TChannels, TBPC> where TChannels : INumOfChannels,new() where TBPC : IBitsPerChannel,new()
{
    public Type ChannelsAttribute
    {
        get { return typeof(TChannels); }
    }

    public Type BitsPerChannelAttribute
    {
        get { return typeof(TBPC); }
    }
}

ifコンストラクトでは、これを次のように利用できます。

var myTex = new Texture<FourChannels, BitsPerChannel_32>();

if (myTex.ChannelsAttribute == typeof(FourChannels))
{
    ... do something with 4 channels
}
else
{
    ... though luck, only 4 channels are supported...
}


最後の注意とアドバイス:
これで問題が解決する場合もありますが、この種の「トリック」に頼るのは通常、設計に欠陥があることを示しています。コードで行った設計上の選択を再検討するのは十分に投資された時間だと思うので、このような松葉杖に頼る必要はありません。

于 2013-10-20T11:13:45.250 に答える
1

C# にはそのような機能はありません。インターフェイスを使用してみたことを述べていますが、方法を指定していません。それらを使用することをお勧めする方法は、複数の制約を持つジェネリックを使用することです。

void Method(T foo) where T : IAttribute1, IAttribute2, IAttribute3, IAttribute4
{
}

そのような属性クラスの 1 つが ICpuMappable であるとしましょう。その場合、以下で使用できる型を制限できますMethod1

void Method1(T foo) where T : ICpuMappable
{
}

fooまた、渡されたものMethod1はすべて CPU マップ可能であることがわかります。

多くのインターフェイスが作成される可能性がありますが、その多くは「フラグ」として扱われるため、維持するのはそれほど難しくありません。

于 2013-10-20T11:06:57.290 に答える