4

T通常、型パラメーターをシール型 (型など) から派生するように制約することはできませんstruct。適合できる型は 1 つしかないため、これは無意味であり、ジェネリックは必要ありません。したがって、次のような制約があります。

where T : string

また:

where T : DateTime

非常に正当な理由で違法です。

ただし、別の型パラメーターに制約する場合、その他の型パラメーターが実際の型に "置換" された (たまたまシールされた) 場合に、これが発生することがあります。クラスを考えてみましょう:

abstract class ExampleBase<TFromType>
{
  internal abstract void M<TFromMethod>(TFromMethod value) where TFromMethod : TFromType;
}

これは非常に無実です。具体化では:

class ExampleOne : ExampleBase<string>
{
  internal override void M<TFromMethod>(TFromMethod strangeString)
  {
    var a = string.IsNullOrEmpty(strangeString);
    Console.WriteLine(a);
    var b = strangeString.Substring(10, 2);
    Console.WriteLine(b);
  }
}

TFromType等しくしstringます。これは意味があるかもしれません。他のメンバー以外M<>の ただし、M<>それ自体は引き続き使用できます: コード:

  var e1 = new ExampleOne();
  e1.M("abcdefghijklmnopqrstuvwxyz");

実行して書き込みます:

間違い
kl

コンソールに。したがって、制約は本質的に になりましwhere TFromMethod : stringたが、それでも問題ありませんでした。

この質問はTFromType、 が値型の場合に何が起こるかについてです。そこで、今回は次のようにします。

class ExampleTwo : ExampleBase<DateTime>
{
  internal override void M<TFromMethod>(TFromMethod strangeDate)
  {
    // var c = DateTime.SpecifyKind(strangeDate, DateTimeKind.Utc);  // will not compile
    // var d = strangeDate.AddDays(66.5);  // will not compile

    var e = string.Format(CultureInfo.InvariantCulture, "{0:D}", strangeDate);  // OK, through boxing
    Console.WriteLine(e);
    var f = object.ReferenceEquals(strangeDate, strangeDate);
    Console.WriteLine("Was 'strangeDate' a box? " + f);
  }
}

では、なぜcandd宣言からの呼び出しが許可されていないのでしょうか? 結局、 .に制約されるstrangeDateコンパイル時の型があります。だから確かに暗黙的に? 結局のところ、これは(上記)で機能しました。TFromMethodDateTimestrangeDateDateTimestringclass ExampleOne

公式のC#言語仕様の関連する場所を参照した回答を希望します。

を追加しようとするときに...dと入力strangeDate.Adすると、IntelliSense (Visual Studio のオートコンプリート機能) が のすべてのアクセス可能なインスタンス メンバーのリストを表示するDateTimeので、明らかに IntelliSense は呼び出しdが正当であると判断することに注意してください!

もちろん、cdをコメントアウトした後、 ExampleTwo(withef) と次のコードを使用できます。

  var e2 = new ExampleTwo();
  e2.M(new DateTime(2015, 2, 13));

実行して書き出す:

2015 年 2 月 13 日金曜日
「strangeDate」はボックスでしたか? 間違い
4

2 に答える 2

2

C# 5.0 仕様を引用するには:

6.1.10 型パラメータを含む暗黙の変換

特定の型パラメーターに対して、次の暗黙的な変換が存在しますT

  • からTその有効な基本クラスCへ、からTの任意の基本クラスへC、および からTによって実装される任意のインターフェイスへC。[...]

  • [...]

10.1.5 型パラメータの制約

型パラメーターの有効な基本クラスは、T次のように定義されます。

  • [...]
  • クラス型T制約がなく、1 つ以上の型パラメーター制約がある場合、その有効な基本クラスは、その型パラメーター制約の有効な基本クラスのセットに最も含まれる型 (§6.4.2) です。一貫性ルールにより、そのような最も包括的なタイプが存在することが保証されます。
  • [...]

これらの規則の目的のために、 value-typeTである制約Vがある場合は、代わりにその最も具体的な基本型であるclass-typeを使用します。これは、明示的に指定された制約では決して発生しませんが、ジェネリック メソッドの制約がオーバーライド メソッド宣言またはインターフェイス メソッドの明示的な実装によって暗黙的に継承される場合に発生する可能性があります。V

これらの規則により、有効な基底クラスが常にclass-typeになることが保証されます。

言い換えると、 のwhere U : T制約が与えられた場合T = string、 の有効な基本クラスはUですstringwhere U : Tの制約が与えられた場合T = DateTime、 の有効な基底クラスUDateTimeではなくValueTypeです。また、型パラメーターの唯一の関連する暗黙的な変換は、型パラメーターの型からその有効な基底クラスへの変換です。

あなたが見つけたように、これはかなり奇妙な動作につながるように見えますが、それにもかかわらず、あなたが見たように動作するように明示的に綴られているので、それは意識的な決定であったに違いありません.

この作業を行うと、コンパイラで問題が発生し、コンパイラがそのような場合に参照型を扱っていると想定する場合があり、それを機能させることには最小限の利点しかないと思います。ただし、それは単なる推測です。

于 2015-02-17T15:24:03.623 に答える
0

C#5 の章13.4.3 Implementation of generic methodsでは、次のように述べられています。

interface I<C>
{
    void H<T>(T t) where T: C;
}

class C: I<string>
{
    void H<T>(T t) 
    {
        string s = t;   // Ok
    }
}

彼らが言うには :

T は T: string の制約を継承しているため、t から s への代入は有効であることに注意してください。ただし、この制約はソース コードでは表現できません。

私はこれを次のように理解しています。

ここで、 String は参照型です。 を記述するs = tと、参照を割り当てwhere T: Cます。制約により許可されているため、割り当ては有効です。

DateTime の場合、 を記述するときにs = t、参照ではなく値をコピーします。

于 2015-02-13T13:15:46.660 に答える