0Pricing
C# Academy · Урок

Обобщенная математика (обзор) и обобщенные атрибуты (при необходимости)

Эмулируйте обобщенную математику без современных возможностей: передавайте поставщик операций или делегаты, тщательно ограничивайте типы и имитируйте «обобщенные атрибуты» с помощью параметров Type.

«Обобщенная математика (обзор) и обобщенные атрибуты (при необходимости)» — бесплатный урок C# Academy на CoddyKit. Это урок 2 из 3. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения C# Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс C# Academy содержит 3 уроков всего.

Обобщённая математика в C# 6

Цель: писать обобщённые числовые алгоритмы в C# 6.

  • Статические абстрактные операторы для T отсутствуют
  • Обходные решения: интерфейс поставщика операций или небольшие делегаты
  • Для аннотаций используйте атрибуты с параметрами Type

Шаблон поставщика операций

Предоставьте ноль и 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);
    }
  }
}

Советы и варианты

Советы:

  • Предпочитайте поставщика операций для повторно используемых числовых алгоритмов.
  • Используйте делегаты для небольших вспомогательных методов (объединение, преобразование).
  • Для аннотаций передавайте Type в атрибутах и считывайте его через отражение.

Обходное решение для обобщённой математики

Быстрая проверка: как практически написать обобщённый Sum без современной обобщённой математики в C# 6?

Повторение

Повторение: если современной обобщённой математики нет, передавайте алгоритмам операции или делегаты. Для аннотаций используйте атрибуты, содержащие Type, и считывайте его через отражение.

Часто задаваемые вопросы

Урок «Обобщенная математика (обзор) и обобщенные атрибуты (при необходимости)» бесплатный?

Да — полный текст урока «Обобщенная математика (обзор) и обобщенные атрибуты (при необходимости)» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс C# Academy, подпишись на CoddyKit PRO. Курс C# Academy содержит 3 уроков всего.

Чему я научусь в уроке «Обобщенная математика (обзор) и обобщенные атрибуты (при необходимости)»?

Эмулируйте обобщенную математику без современных возможностей: передавайте поставщик операций или делегаты, тщательно ограничивайте типы и имитируйте «обобщенные атрибуты» с помощью параметров Type. Ты практикуешь C# Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать C# Academy?

Предыдущий опыт не требуется. C# Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 3.

Сколько времени занимает урок «Обобщенная математика (обзор) и обобщенные атрибуты (при необходимости)»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке C# Academy?

Да. Каждый урок C# Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. unmanaged, notnull, new() (эмуляция в C# 6)
  2. Обобщенная математика (обзор) и обобщенные атрибуты (при необходимости)
  3. Повторно используемые обобщенные утилиты
← Назад к C# Academy