0Pricing
C# Academy · レッスン

ジェネリック数学(概要)とジェネリック属性(関連する場合)

最新機能を使わずにジェネリック数学を再現します。演算プロバイダーまたはdelegateを渡し、型を慎重に制約し、Typeパラメーターで「ジェネリック属性」を模倣します。

「ジェネリック数学(概要)とジェネリック属性(関連する場合)」はCoddyKit上の無料C# Academyレッスンです。 これはレッスン2/3です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはC# Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 C# Academyコースには全3レッスンが含まれています。

C# 6でのジェネリック数値演算

目標: C# 6で数値アルゴリズムをジェネリックに記述します。

  • Tに対するstatic abstract演算子はありません
  • 回避策: ops-providerインターフェースまたは小さなデリゲート
  • アノテーションには、Typeパラメーターを持つ属性を使います

Ops-providerパターン

インターフェースを通じてZeroとAddを提供します。ジェネリックアルゴリズムはTに対して+を使う代わりに、これらのメソッドを呼び出します。

using System;
using System.Collections.Generic;

public interface INumericOps<T>
{
  T Zero { get; }
  T Add(T a, T b);
}

public sealed class IntOps : INumericOps<int>
{
  public int Zero { get { return 0; } }
  public int Add(int a, int b) { return a + b; }
}

public sealed class DoubleOps : INumericOps<double>
{
  public double Zero { get { return 0.0; } }
  public double Add(double a, double b) { return a + b; }
}

public class Program
{
  static T Sum<T>(IEnumerable<T> items, INumericOps<T> ops)
  {
    T acc = ops.Zero;
    foreach (T x in items) acc = ops.Add(acc, x);
    return acc;
  }

  public static void Main(string[] args)
  {
    int s1 = Sum(new int[] {1,2,3}, new IntOps());
    double s2 = Sum(new double[] {1.5, 2.0}, new DoubleOps());
    Console.WriteLine("Sum int = " + s1);
    Console.WriteLine("Sum double = " + s2);
  }
}

ヘルパーを使った平均値

除算のようにジェネリックに表現できない演算については、小さなコンバーターまたは追加のデリゲートを渡します。

using System;
using System.Collections.Generic;

public interface INumericOps<T>
{
  T Zero { get; }
  T Add(T a, T b);
}

public sealed class DoubleOps : INumericOps<double>
{
  public double Zero { get { return 0.0; } }
  public double Add(double a, double b) { return a + b; }
}

public class Program
{
  static double Average<T>(IEnumerable<T> items, INumericOps<T> ops, Func<T,double> toDouble)
  {
    T acc = ops.Zero;
    int count = 0;
    foreach (T x in items) { acc = ops.Add(acc, x); count++; }
    if (count == 0) throw new ArgumentException("Empty sequence", "items");
    // convert with a delegate for division
    return toDouble(acc) / count;
  }

  public static void Main(string[] args)
  {
    double avg = Average<double>(new double[] {2.0, 4.0, 6.0}, new DoubleOps(), delegate(double d) { return d; });
    Console.WriteLine("Avg = " + avg);
  }
}

デリゲートによる畳み込み

デリゲートを使うと最小限にできます。初期値と結合関数を渡します。小さなユーティリティに適しています。

using System;

public class Program
{
  static T Fold<T>(T[] xs, T seed, Func<T,T,T> combine)
  {
    T acc = seed;
    for (int i = 0; i < xs.Length; i++) acc = combine(acc, xs[i]);
    return acc;
  }

  public static void Main(string[] args)
  {
    int sum = Fold<int>(new int[] {1,2,3}, 0, delegate(int a, int b) { return a + b; });
    double sumD = Fold<double>(new double[] {1.5, 2.5}, 0.0, delegate(double a, double b) { return a + b; });
    Console.WriteLine("Sum ints = " + sum);
    Console.WriteLine("Sum doubles = " + sumD);
  }
}

属性の回避策

C# 6には真のジェネリック属性がありません。関連するジェネリック型を示すため、属性にTypeを渡します。

using System;
using System.Reflection;

// C# 6 cannot declare generic attributes. Workaround: pass a Type.
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public sealed class ValidatorForAttribute : Attribute
{
  public Type TargetType { get; private set; }
  public ValidatorForAttribute(Type targetType) { TargetType = targetType; }
}

public sealed class Customer { public string Name; }

[ValidatorFor(typeof(Customer))]
public sealed class CustomerValidator
{
  public bool IsValid(Customer c) { return c != null && !string.IsNullOrEmpty(c.Name); }
}

public class Program
{
  public static void Main(string[] args)
  {
    Type t = typeof(CustomerValidator);
    object[] attrs = t.GetCustomAttributes(typeof(ValidatorForAttribute), false);
    if (attrs.Length > 0)
    {
      ValidatorForAttribute a = (ValidatorForAttribute)attrs[0];
      Console.WriteLine("Validator targets: " + a.TargetType.Name);
    }
  }
}

ヒントと選択

ヒント:

  • 再利用可能な数値アルゴリズムにはops-providerを優先します。
  • 小さなヘルパー(結合、変換)にはデリゲートを使います。
  • アノテーションでは属性にTypeを渡し、リフレクションで読み取ります。

ジェネリック数値演算の回避策

確認問題: C# 6で、最新のジェネリック数値演算を使わずにジェネリックなSumを記述する実用的な方法は何ですか?

振り返り

振り返り: 最新のジェネリック数値演算がない場合は、アルゴリズムにopsまたはデリゲートを渡します。アノテーションにはTypeを保持する属性を使い、リフレクションで読み取ります。

よくある質問

「ジェネリック数学(概要)とジェネリック属性(関連する場合)」レッスンは無料ですか?

はい。「ジェネリック数学(概要)とジェネリック属性(関連する場合)」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、C# Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 C# Academyコースには全3レッスンが含まれています。

「ジェネリック数学(概要)とジェネリック属性(関連する場合)」で何を学びますか?

最新機能を使わずにジェネリック数学を再現します。演算プロバイダーまたはdelegateを渡し、型を慎重に制約し、Typeパラメーターで「ジェネリック属性」を模倣します。 ブラウザで直接実行するハンズオンコードでC# Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

C# Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのC# Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/3です。

「ジェネリック数学(概要)とジェネリック属性(関連する場合)」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このC# Academyレッスンでコードを書いて実行できますか?

はい。すべてのC# Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. unmanaged、notnull、new()(C# 6でのエミュレーション)
  2. ジェネリック数学(概要)とジェネリック属性(関連する場合)
  3. 再利用可能なジェネリックユーティリティ
← C# Academyに戻る