Форум программистов, компьютерный форум, киберфорум
C# .NET
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 5.00/9: Рейтинг темы: голосов - 9, средняя оценка - 5.00
107 / 107 / 9
Регистрация: 19.12.2010
Сообщений: 417

Объявление обобщённого типа (Generic) для стандартных целочисленных типов

14.08.2013, 14:57. Показов 1921. Ответов 12
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Здравствуйте.
Есть класс для вычисления факториала:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
    public class Factorial
    {
        long _digit;
        public Factorial(long digit)
        {
            Digit = _digit;
        }
 
        public long Digit
        {
            get
            {
                return _digit;
            }
            set
            {
                Validate(value);
 
                _digit = value;
                Calculate();
            }
        }
        public long Value { get; private set; }
 
        public void Calculate()
        {
            Value = Calculate(_digit);
        }
        public static long Calculate(long digit)
        {
            Validate(digit);
            long factorial = 1; // значение факториала
            for (long i = 2; i <= digit; i++) // цикл начинаем с 2, т.к. нет смысла начинать с 1
            {
                factorial = factorial * i;
            }
            return factorial;
        }
 
        private static void Validate(long digit)
        {
            if (digit <= 0)
            {
                throw new ArgumentException("Digit can't be nonpositive.", "digit");
            }
        }
Хочу сделать его обобщённым типом, доступным только для стандартных целочисленных типов: int/Int32/Int64, long и т.д.
1. Возможно ли это сделать красиво, нормально?
Нашёл что-то близкое тут: и Generic, определение типа переменной во время выполнения программы
Но ничего конкретного и красивого...
2. Вот почему Microsoft не могло сделать стандартные типы наподобие Int32 классами, а не структурами, и унаследовать их от общего интерфейса, скажем, от IIntegralDigit?
Заранее спасибо.
0
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
14.08.2013, 14:57
Ответы с готовыми решениями:

Найти предельные значения для целочисленных типов. Не использовать заранее определенные константы границ типов.
Буду благодарен! Найти предельные значения для целочисленных типов. Не использовать заранее определенные константы границ типов.

Как вызвать деструктор для обобщенного типа
Здравствуйте. У меня проблема с вызовом деструктора для обобщенного типа. Такой код: class MyClass&lt;TData&gt; { ...

Вызов оператора false для объекта обобщенного типа
Добрый вечер. По заданию нужно было перегрузить оператор false(как понимаю, с этим я справился). Но вот как теперь вызвать этот оператор?...

12
 Аватар для Ant-kul
7 / 7 / 2
Регистрация: 06.08.2010
Сообщений: 109
14.08.2013, 16:52
Цитата Сообщение от FutureCome Посмотреть сообщение
Хочу сделать его обобщённым типом, доступным только для стандартных целочисленных типов: , long и т.д.
Можно сделать расширение для типов int, и тогда факториал будет доступный по ссылке через точку.

Добавлено через 7 минут
C#
1
2
3
4
5
6
7
8
public static class Extensions
{
    public static int Factorial(this int digit)
    {
        //чтото делаете
        return res;
    }
}
Тогда в тексте просто пишите
C#
1
2
int i =5;
int fact = i.Factorial();
1
107 / 107 / 9
Регистрация: 19.12.2010
Сообщений: 417
14.08.2013, 19:32  [ТС]
Так ведь мне так придётся писать отдельную реализацию для каждого стандартного типа...?
Ибо с generic'ами такое не прокатывает:
C#
1
T factorial = 1;
0
 Аватар для Anklav
447 / 305 / 47
Регистрация: 23.01.2013
Сообщений: 661
14.08.2013, 20:45
Ограничиваете обобщение интерфейсом IConvertible, приводите входную величину допустим к Decimal, ищите факториал и возвращаете. Лучше приводить к максимально вместимому типу, что б не терять данных.

C#
1
2
3
4
5
6
7
        T Factorial<T>(T value) where T : IConvertible
        {
            IConvertible conv = (IConvertible)value;
            decimal d = conv.ToDecimal(NumberFormatInfo.CurrentInfo);
            /*делаете работу*/
            return (T)(IConvertible)d;
        }
1
107 / 107 / 9
Регистрация: 19.12.2010
Сообщений: 417
14.08.2013, 21:15  [ТС]
Спасибо. Этот способ, конечно, неплох... Но конвертировать туда и обратно... Тоже не очень.
Нет способа по-адекватнее?)
Что ж мелкософт такой непродуманный?
0
 Аватар для Anklav
447 / 305 / 47
Регистрация: 23.01.2013
Сообщений: 661
14.08.2013, 21:46
Ну каждый метод в ручную тогда пишите, все равно простых типов не так много.

Я тут кстати при втором приведении ошибся, так метод будет работать:

C#
1
2
3
4
5
6
7
8
9
10
    static class ConvertibleExtensions
    {
        public static T Factorial<T>(this T value) where T : IConvertible
        {
            IConvertible conv = (IConvertible)value;
            decimal d = conv.ToDecimal(NumberFormatInfo.CurrentInfo);
            /*делаете работу*/
            return (T)((IConvertible)d).ToType(typeof(T), NumberFormatInfo.CurrentInfo);
        }
    }
1
Master of Orion
Эксперт .NET
 Аватар для Psilon
6102 / 4958 / 905
Регистрация: 10.07.2011
Сообщений: 14,522
Записей в блоге: 5
14.08.2013, 21:51
FutureCome, и какие методы должны быть у этого интерфейсА?
1
438 / 362 / 100
Регистрация: 29.06.2010
Сообщений: 981
Записей в блоге: 1
15.08.2013, 11:39
Есть решение с generic.

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
    public class Factorial<TValue> where TValue : struct
    {
        private TValue _digit;
 
        public Factorial(TValue digit)
        {
            _digit = digit;
        }
 
        public TValue Digit
        {
            get { return _digit; }
            set
            {
                Validate(value);
 
                _digit = value;
                Calculate();
            }
        }
 
        public long Value { get; private set; }
 
        public void Calculate()
        {
            Value = Calculate(_digit);
        }
 
        public static long Calculate(TValue digit)
        {
            int digitInt = Validate(digit);
            long factorial = 1; // значение факториала
            for (long i = 2; i <= digitInt; i++) // цикл начинаем с 2, т.к. нет смысла начинать с 1
            {
                factorial = factorial*i;
            }
            return factorial;
        }
 
        private static int Validate(TValue digit)
        {
            int result;
            if (!int.TryParse(digit.ToString(), out result))
            {
                throw new ArgumentException("Digit can't be nonpositive.", "digit");
            }
            return result;
        }
    }
Но у данного решения есть один минус, в качестве типа можно запихнуть как нужные так и ненужные типы (дату и собственную структуру).

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
        private static void Main(string[] args)
        {
            new Factorial<int>(112).Calculate();
            new Factorial<double>(112.12).Calculate();
            new Factorial<decimal>((decimal) 112.45).Calculate();
 
            //Вот это тоже можно
            new Factorial<bool>(false).Calculate();
            new Factorial<MyStruct>(new MyStruct()).Calculate();
            new Factorial<DateTime>(DateTime.Now).Calculate();
 
           //Ошибка
            new Factorial<Program>(new Program()).Calculate();
        }
 
        public struct MyStruct
        {
        }
Добавлено через 8 минут
Можно еще вот так

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public class Factorial
    {
        private ValueType _digit;
 
        public Factorial(ValueType digit)
        {
            _digit = digit;
        }
 
        public ValueType Digit
        {
            get { return _digit; }
            set
            {
                Validate(value);
 
                _digit = value;
                Calculate();
            }
        }
 
        public long Value { get; private set; }
 
        public void Calculate()
        {
            Value = Calculate(_digit);
        }
 
        public static long Calculate(ValueType digit)
        {
            int digitInt = Validate(digit);
            long factorial = 1; // значение факториала
            for (long i = 2; i <= digitInt; i++) // цикл начинаем с 2, т.к. нет смысла начинать с 1
            {
                factorial = factorial*i;
            }
            return factorial;
        }
 
        private static int Validate(ValueType digit)
        {
            int result;
            if (!int.TryParse(digit.ToString(), out result))
            {
                throw new ArgumentException("Digit can't be nonpositive.", "digit");
            }
            return result;
        }
    }
Но опять данный пример не лишен недостатков предыдущего
1
107 / 107 / 9
Регистрация: 19.12.2010
Сообщений: 417
15.08.2013, 13:08  [ТС]
Спасибо всем.
Цитата Сообщение от Psilon Посмотреть сообщение
FutureCome, и какие методы должны быть у этого интерфейсА?
Даже и не знаю... Сходу не скажу...
На секунду появилась идея: пустой интерфейс типа IIntegralNumber и реализовывать его только стандартными целочисленными типами, но так его может реализовать любой, что неправильно. Наследовать от какого-то класса IntegralNumber - тоже не очень.
Вероятно, Вы, Psilon, намекаете, что невозможно выделить общие и идентифицирующие поля... Может быть, но проблема подобного рода существует, а значит её нужно решать...
0
Master of Orion
Эксперт .NET
 Аватар для Psilon
6102 / 4958 / 905
Регистрация: 10.07.2011
Сообщений: 14,522
Записей в блоге: 5
15.08.2013, 14:15
FutureCome,
ероятно, Вы, Psilon, намекаете, что невозможно выделить общие и идентифицирующие поля...
А вы, однако, неплохо читаете между строк Это не моэет не радовать. Решать нужно. Но как решить - пока и сам не знаю, но интерфейс для этого не очень подходит. Все-таки ООП принципиально новая ветвь развития (хотя в том же С есть очень много похожего, типа инкапсуляции и полиморфизма, но в ООП это все переложено на язык/среду). Ждем очередной такой же. Все же программирование это не "серебренная пуля", тут есть нерешенные проблемы.

Добавлено через 4 минуты
FutureCome, смысл в том, что данные типы различаются не методами, а структурой: занимаемым размером, областью принимаемых значений, и т.д. А типа с разной структурой не могут наследоваться от интерфейса, у которого есь только методы MinValue и MaxValue. Потому что даже более простые вещи не могут сделать. Например, если мы спомощью рефлексии хотим установить значение поля или структуры, мы не можем написать
C#
1
2
3
4
5
var forp = myType.GetMember(blabla) as IFieldOrProperty 
if (forp != null)
{
  forp.SetValue(...);
}
но нет. Мы должны делать так
C#
1
2
3
4
5
6
7
var field = myType.GetField(blabla);
if (field == null)
{
   var property = myType.GetProperty(blabla);
   if (property != null)
    ...
}
1
107 / 107 / 9
Регистрация: 19.12.2010
Сообщений: 417
15.08.2013, 15:39  [ТС]
Цитата Сообщение от Psilon Посмотреть сообщение
Все-таки ООП принципиально новая ветвь развития (хотя в том же С есть очень много похожего, типа инкапсуляции и полиморфизма, но в ООП это все переложено на язык/среду).
Psilon, как-то странно Вы отделили инкапсуляцию и полиморфизм от ООП. Ведь эти 2 вещи + часть других (наследование, напр.) являются основными понятиями ООП... В чистом C их не было, насколько я помню, они пришли с Object C или с C++.
0
Master of Orion
Эксперт .NET
 Аватар для Psilon
6102 / 4958 / 905
Регистрация: 10.07.2011
Сообщений: 14,522
Записей в блоге: 5
15.08.2013, 16:11
FutureCome, как вы думаете, ООП было придумано внезапным прозрением избранных программистов, или это все же был итеративный процесс движения от более хрупких архитектур к менее хрупким?

Инкапсуляция в структурных языках наиболее легко видна на примере языка Pascal: функции и переменные, объявленные в разделе implementation не видны никому снаружи модуля, но видны для всех функцих в пределах этого модуля. Для взаимодействия с внешней средой есть раздел interface, который предоставляет доступ к некоторым методам из раздела implementation. Ничего не напоминает?

Полиморфизм же легко виден на следующем примере (опять же, чистый С):
Кликните здесь для просмотра всего текста
Язык программирования С не имеет синтаксических конструкций для программирования в парадигме ООП, но в данном случае на помощь приходят структуры и указатели на функции.

Рендер оперирует обобщенными структурами (Object3d), содержащими указатель на область данных, в которой хранятся фактические параметры 3D объекта, а также указатели на функции, которые умеют правильным образом обрабатывать эту область данных.
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
typedef 
struct {
    // указатель на область данных, содержащую параметры конкретного примитива
    // в случае полигона: координаты вершин, цвет (или битмапка с текстурой), свойства материала
    // в случае сферы: координаты центра, радиус, и т.п.
    void * data;
    
    // указатель на функцию, которая умеет обрабатывать примитив,
    // на который ссылается указатель data
    Boolean (*intersect)(const void * data,
                         const Point3d ray_start_point,
                         const Vector3d ray,
                         // сюда будет сохранятся точка пересечения луча и примитива
                         Point3d * const intersection_point);
 
    // получение цвета в точке пересечения
    // здесь можно возвращать просто цвет объекта
    // или обеспечить процедурную генерацию текстуры
    // или использовать загруженную из файла текстуру :)
    Color (*get_color)(const void * data,
                       const Point3d intersection_point);
 
    // получение вектора нормали в точке пересечения (используется для рассчёта освещённости)
    // в случае сферы и полигона - вектор нормали можно рассчитать аналитически
    // как вариант, вместо аналитечских рассчётов - объект может содержать карту нормалей
    Vector3d (*get_normal_vector)(const void * data,
                                  const Point3d intersection_point);
 
    // вызывается рендером перед удалением Object3d
    // тут можно освобождать ресурсы, связанные с объектом
    // например, удалять текстуры
    void (*release_data)(void * data);
}

Такой подход позволяет локализировать код относящийся к каждому 3D примитиву в отдельном файле. Следовательно, довольно легко вносить изменения в структуры 3D объектов (например, добавить поддержку текстур или карт нормалей), или даже добавлять новые 3D примитивы. При этом — не нужно вносить изменения в код рендера. Получилось что-то на подобии инкапсуляции.

C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
// ... инклуды
 
typedef
struct {
    Point3d center;
    Float radius;
    Color color;
}
Sphere;
 
// ... декларации функций
 
// "конструктор" сферы
Object3d *
new_sphere(const Point3d center,
           const Float radius,
           const Color color) {
    
    Sphere * sphere = malloc(sizeof(Sphere));
    sphere->center = center;
    sphere->radius = radius;
    sphere->color = color;
 
    // упаковываем сферу в обобщённую структуру 3D объекта
    Object3d * obj = malloc(sizeof(Object3d));
    obj->data = sphere;
 
    // добавляем функции, которые умеют работать со структурой Sphere
    obj->get_color = get_sphere_color;
    obj->get_normal_vector = get_sphere_normal_vector;
    obj->intersect = intersect_sphere;
    obj->release_data = release_sphere_data;
    
    return obj;
}
 
// цвет сферы всюду одинаковый
// но здесь можно реализовать процедурную генерацию текстуры
static Color
get_sphere_color(const void * data,
                 const Point3d intersection_point) {
    const Sphere * sphere = data;
    return sphere->color;
}
 
// вычисляем аналитически вектор нормали в точке на поверхности сферы
// сюда можно впилить Bump Mapping
static Vector3d
get_sphere_normal_vector(const void * data,
                         const Point3d intersection_point) {
    const Sphere * sphere = data;    
    // vector3dp - служебная функция, которая создаёт вектор по двум точкам
    Vector3d n = vector3dp(sphere->center, intersection_point);
    normalize_vector(&n);
    return n;
}
 
// пересечение луча и сферы
static Boolean
intersect_sphere(const void * data,
                 const Point3d ray_start_point,
                 const Vector3d ray,
                 Point3d * const intersection_point) {
    const Sphere * sphere = data;    
    // чтобы найти точку пересечения луча и сферы - нужно решить квадратное уравнение
    // полную реализацию метода можно найти в исходниках на GitHub
}
 
// "деструктор" сферы
void
release_sphere_data(void * data) {
    Sphere * sphere = data;
    // если бы сфера содержала текстуры - их нужно было бы здесь освободить
    free(sphere);
}
1
107 / 107 / 9
Регистрация: 19.12.2010
Сообщений: 417
19.08.2013, 21:19  [ТС]
Хех, можно приляпать BigInteger. Тоже не идеально, но...
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
19.08.2013, 21:19
Помогаю со студенческими работами здесь

Ввод нового типа "очень длинный" на основе двух стандартных типов
Есть тип struct verylong_t { long hi; long lo; }; Думаю о том, как создать его по строке с длинным числом (до 18...

Специализация шаблона для стандартных типов
Пишу динамическую структуру данных (не суть важно какую, допустим для простоты стек). Она работает с объектами типа Box, которые: 1....

Для чего необходимо переопределение стандартных типов?
Добрый вечер, часто сталкиваюсь с переопределением имен стандартных типов особенно в С для микроконтроллеров, вроде такого: ...

Перегрузка операторов для стандартных типов/Сложение char[] и int
Добрый день. Захотелось узнать, а можно ли написать оператор сложения для char и char? Пробовал так: #include &lt;iostream&gt; ...

Определение и использование функций для обработки стандартных типов данных
Заданы три числа. Отрицательные числа заменить абсолютными значениям, нулевые значения – единицами, положительные – увеличить в два раза.


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
13
Ответ Создать тему
Новые блоги и статьи
Инструменты COM: Сохранение данный из VARIANT в файл и загрузка из файла в VARIANT
bedvit 28.01.2026
Сохранение базовых типов COM и массивов (одномерных или двухмерных) любой вложенности (деревья) в файл, с возможностью выбора алгоритмов сжатия и шифрования. Часть библиотеки BedvitCOM Использованы. . .
Загрузка PNG с альфа-каналом на SDL3 для Android: с помощью SDL_LoadPNG (без SDL3_image)
8Observer8 28.01.2026
Содержание блога SDL3 имеет собственные средства для загрузки и отображения PNG-файлов с альфа-каналом и базовой работы с ними. В этой инструкции используется функция SDL_LoadPNG(), которая. . .
Загрузка PNG с альфа-каналом на SDL3 для Android: с помощью SDL3_image
8Observer8 27.01.2026
Содержание блога SDL3_image - это библиотека для загрузки и работы с изображениями. Эта пошаговая инструкция покажет, как загрузить и вывести на экран смартфона картинку с альфа-каналом, то есть с. . .
влияние грибов на сукцессию
anaschu 26.01.2026
Бифуркационные изменения массы гриба происходят тогда, когда мы уменьшаем массу компоста в 10 раз, а скорость прироста биомассы уменьшаем в три раза. Скорость прироста биомассы может уменьшаться за. . .
Воспроизведение звукового файла с помощью SDL3_mixer при касании экрана Android
8Observer8 26.01.2026
Содержание блога SDL3_mixer - это библиотека я для воспроизведения аудио. В отличие от инструкции по добавлению текста код по проигрыванию звука уже содержится в шаблоне примера. Нужно только. . .
Установка Android SDK, NDK, JDK, CMake и т.д.
8Observer8 25.01.2026
Содержание блога Перейдите по ссылке: https:/ / developer. android. com/ studio и в самом низу страницы кликните по архиву "commandlinetools-win-xxxxxx_latest. zip" Извлеките архив и вы увидите. . .
Вывод текста со шрифтом TTF на Android с помощью библиотеки SDL3_ttf
8Observer8 25.01.2026
Содержание блога Если у вас не установлены Android SDK, NDK, JDK, и т. д. то сделайте это по следующей инструкции: Установка Android SDK, NDK, JDK, CMake и т. д. Сборка примера Скачайте. . .
Использование SDL3-callbacks вместо функции main() на Android, Desktop и WebAssembly
8Observer8 24.01.2026
Содержание блога Если вы откроете примеры для начинающих на официальном репозитории SDL3 в папке: examples, то вы увидите, что все примеры используют следующие четыре обязательные функции, а. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru