Доступ к статическим функциям класса генериков в Visual C # [дубликат]

Intro

С моего времени в колледже я запрограммировал в Java, JavaScript, Pascal, ABAP , PHP, Progress 4GL, C / C ++ и, возможно, несколько других языки, о которых я не могу сейчас думать.

. Хотя у всех их есть свои лингвистические особенности, каждый из этих языков разделяет многие из тех же основных понятий. Такие понятия включают процедуры / функции, IF -статы, FOR -loops и WHILE -loops.


Традиционный for -луп

Традиционный цикл for состоит из трех компонентов:

  1. Инициализация: выполняется перед тем, как блок look будет выполнен в первый раз
  2. Условие: проверяет условие каждый раз перед цикл цикла выполняется и завершает цикл, если false
  3. Последующая мысль: выполняется каждый раз после выполнения цикла цикла

Эти три компонента разделены друг от друга на символ ;. Содержимое для каждого из этих трех компонентов является необязательным, что означает, что наиболее минимальный цикл for возможен:

for (;;) {
    // Do stuff
}

Конечно, вам нужно будет включить if(condition === true) { break; } или if(condition === true) { return; } где-то внутри этого for -loop, чтобы заставить его перестать работать.

Обычно, хотя инициализация используется для объявления индекса, условие используется для сравнения этого индекса с минимальным или максимальным значением значение, а afterthought используется для увеличения индекса:

for (var i = 0, length = 10; i < length; i++) {
    console.log(i);
}

Использование традиционного цикла for для циклического перемещения по массиву

Традиционный способ массив, это:

for (var i = 0, length = myArray.length; i < length; i++) {
    console.log(myArray[i]);
}

Или, если вы предпочитаете петлю назад, вы делаете это:

for (var i = myArray.length - 1; i > -1; i--) {
    console.log(myArray[i]);
}

Однако существует множество вариантов, например, для например, этот:

for (var key = 0, value = myArray[key], length = myArray.length; key < length; value = myArray[++key]) {
    console.log(value);
}

... или этот ...

var i = 0, length = myArray.length;
for (; i < length;) {
    console.log(myArray[i]);
    i++;
}

... или этот:

var key = 0, value;
for (; value = myArray[key++];){
    console.log(value);
}

В зависимости от того, что лучше всего работает, в значительной степени зависит как от личного вкуса, так и от конкретного варианта использования.

Обратите внимание, что каждый из этих вариантов

[h27] [h27] while loop

Один из альтернатив цикла for - это цикл while. Для циклического прохождения массива вы можете сделать это:

var key = 0;
while(value = myArray[key++]){
    console.log(value);
}

Как и обычные петли for, петли while поддерживаются даже самым старым из браузеров.

Кроме того, обратите внимание, что цикл while может быть переписан в виде цикла for. Например, петля while здесь ведет себя точно так же, как это for -loop:

for(var key = 0; value = myArray[key++];){
    console.log(value);
}

For...in и for...of

In JavaScript, вы также можете сделать это:

for (i in myArray) {
    console.log(myArray[i]);
}

Это должно использоваться с осторожностью, однако, поскольку оно не ведет себя так же, как традиционный цикл for во всех случаях, и есть потенциал побочные эффекты, которые необходимо учитывать. См. . Почему использование & quot; для ... в & quot;

В качестве альтернативы for...in теперь есть и для for...of . ]. В следующем примере показана разница между циклом for...of и контуром for...in:

var myArray = [3, 5, 7];
myArray.foo = "hello";

for (var i in myArray) {
  console.log(i); // logs 0, 1, 2, "foo"
}

for (var i of myArray) {
  console.log(i); // logs 3, 5, 7
}

Кроме того, вам необходимо учитывать, что никакая версия Internet Explorer не поддерживает for...of ( Edge 12 + ) и что для for...in требуется, по меньшей мере, Internet & nbsp; Explorer & nbsp; 10.


Array.prototype.forEach()

Альтернатива for -loops Array.prototype.forEach() , который использует следующий синтаксис:

myArray.forEach(function(value, key, myArray) {
    console.log(value);
});

Array.prototype.forEach() поддерживается всеми современными браузерами, а также Internet & nbsp; Explorer & nbsp; 9 и более поздними версиями.


Библиотеки

Наконец, во многих библиотеках утилиты также есть свой вариант foreach. AFAIK, три наиболее популярных из них:

jQuery.each() , в jQuery :

$.each(myArray, function(key, value) {
    console.log(value);
});

_.each() , в Underscore.js :

_.each(myArray, function(value, key, myArray) {
    console.log(value);
});

_.forEach() , в Lodash.js :

_.forEach(myArray, function(value, key) {
    console.log(value);
});
301
задан poke 16 December 2015 в 10:40
поделиться

17 ответов

Хейльсберг рассказал о причинах отказа от реализации функции в интервью с Брюсом Эккелем.

Я должен признать, однако, что я не знаю, как он думает его предлагаемое решение будет работать. Его предложение - отложить арифметические операции до какого-то другого родового класса (прочитайте интервью!). Как это помогает? ИМХО, не так много.

121
ответ дан Konrad Rudolph 25 August 2018 в 12:08
поделиться

Этот вопрос представляет собой немного часто задаваемых вопросов, поэтому я публикую это как wiki (так как раньше я писал аналогично, но это более старый); в любом случае ...

Какую версию .NET вы используете? Если вы используете .NET 3.5, то у меня есть реализация общих операторов в MiscUtil (бесплатно и т. Д.).

У этого есть методы, такие как T Add<T>(T x, T y) , и другие варианты арифметики для разных типов (например, DateTime + TimeSpan).

Кроме того, это работает для всех встроенных, снятых и сделанных на заказ операторов и кэширует делегат для выполнения.

Некоторые дополнительные сведения о том, почему это сложно, это здесь .

Вы также можете знать, что метод dynamic (4.0) также разрешает эту проблему косвенно, т. Е.

dynamic x = ..., y = ...
dynamic result = x + y; // does what you expect
16
ответ дан 2 revs 25 August 2018 в 12:08
поделиться

Какова точка упражнения?

Как уже указывали люди, у вас может быть функция, не имеющая общих функций, с наибольшим элементом, а компилятор автоматически преобразует меньшие ints для вас.

static bool IntegerFunction(Int64 value) { }

Если ваша функция находится на критическом по производительности пути (очень маловероятно, IMO), вы можете обеспечить перегрузку для всех необходимых функций.

static bool IntegerFunction(Int64 value) { }
...
static bool IntegerFunction(Int16 value) { }
1
ответ дан dbkk 25 August 2018 в 12:08
поделиться

Пока нет «хорошего» решения. Однако вы можете значительно сузить аргумент типа, чтобы исключить многие ошибки для вашего гипотетического «INumeric» ограничения, как показано выше.

static bool IntegerFunction & lt; T & gt; (значение T), где T: IComparable, IFormattable, IConvertible, IComparable & lt; T & gt ;, IEquatable & lt; T & gt ;, struct {...

1
ответ дан dmihailescu 25 August 2018 в 12:08
поделиться

Если вы используете .NET 4.0 и более поздние версии, вы можете просто использовать динамический аргумент метода и проверить во время выполнения, что переданный тип динамического аргумента является числовым / целым типом.

Если тип переданного динамический не является числовым / целочисленным типом, а затем генерирует исключение.

Примерный короткий код, реализующий идею, выглядит примерно так:

using System;
public class InvalidArgumentException : Exception
{
    public InvalidArgumentException(string message) : base(message) {}
}
public class InvalidArgumentTypeException : InvalidArgumentException
{
    public InvalidArgumentTypeException(string message) : base(message) {}
}
public class ArgumentTypeNotIntegerException : InvalidArgumentTypeException
{
    public ArgumentTypeNotIntegerException(string message) : base(message) {}
}
public static class Program
{
    private static bool IntegerFunction(dynamic n)
    {
        if (n.GetType() != typeof(Int16) &&
            n.GetType() != typeof(Int32) &&
            n.GetType() != typeof(Int64) &&
            n.GetType() != typeof(UInt16) &&
            n.GetType() != typeof(UInt32) &&
            n.GetType() != typeof(UInt64))
            throw new ArgumentTypeNotIntegerException("argument type is not integer type");
        //code that implements IntegerFunction goes here
    }
    private static void Main()
    {
         Console.WriteLine("{0}",IntegerFunction(0)); //Compiles, no run time error and first line of output buffer is either "True" or "False" depends on the code that implements "Program.IntegerFunction" static method.
         Console.WriteLine("{0}",IntegerFunction("string")); //Also compiles but it is run time error and exception of type "ArgumentTypeNotIntegerException" is thrown here.
         Console.WriteLine("This is the last Console.WriteLine output"); //Never reached and executed due the run time error and the exception thrown on the second line of Program.Main static method.
    }

Конечно, это решение работает только во время выполнения но никогда во время компиляции.

Если вы хотите, чтобы решение всегда работало во время компиляции и никогда не было во время выполнения, вам придется обернуть динамику с помощью открытого struct / class, чьи перегруженные публичные конструкторы принимают аргументы только желаемые типы и присвоить соответствующее имя структуры / класса.

Имеет смысл, что обернутая динамика всегда является частным членом класса / структуры и является единственным членом struct / class и именем Единственным членом struct / class является «value».

Вам также необходимо будет определить и реализовать общедоступные методы и / или операторы которые работают с желаемыми типами для частного динамического члена класса / структуры, если это необходимо.

Также имеет смысл, что struct / class имеет специальный / уникальный конструктор, который принимает динамический аргумент, который инициализирует его только private динамический член, называемый «значение», но модификатор этого конструктора, конечно, является закрытым.

После того, как класс / структура будет готов, тип аргумента IntegerFunction будет определяться как определенный класс / структура.

Пример длинного кода, который реализует идею, выглядит примерно так:

using System;
public struct Integer
{
    private dynamic value;
    private Integer(dynamic n) { this.value = n; }
    public Integer(Int16 n) { this.value = n; }
    public Integer(Int32 n) { this.value = n; }
    public Integer(Int64 n) { this.value = n; }
    public Integer(UInt16 n) { this.value = n; }
    public Integer(UInt32 n) { this.value = n; }
    public Integer(UInt64 n) { this.value = n; }
    public Integer(Integer n) { this.value = n.value; }
    public static implicit operator Int16(Integer n) { return n.value; }
    public static implicit operator Int32(Integer n) { return n.value; }
    public static implicit operator Int64(Integer n) { return n.value; }
    public static implicit operator UInt16(Integer n) { return n.value; }
    public static implicit operator UInt32(Integer n) { return n.value; }
    public static implicit operator UInt64(Integer n) { return n.value; }
    public static Integer operator +(Integer x, Int16 y) { return new Integer(x.value + y); }
    public static Integer operator +(Integer x, Int32 y) { return new Integer(x.value + y); }
    public static Integer operator +(Integer x, Int64 y) { return new Integer(x.value + y); }
    public static Integer operator +(Integer x, UInt16 y) { return new Integer(x.value + y); }
    public static Integer operator +(Integer x, UInt32 y) { return new Integer(x.value + y); }
    public static Integer operator +(Integer x, UInt64 y) { return new Integer(x.value + y); }
    public static Integer operator -(Integer x, Int16 y) { return new Integer(x.value - y); }
    public static Integer operator -(Integer x, Int32 y) { return new Integer(x.value - y); }
    public static Integer operator -(Integer x, Int64 y) { return new Integer(x.value - y); }
    public static Integer operator -(Integer x, UInt16 y) { return new Integer(x.value - y); }
    public static Integer operator -(Integer x, UInt32 y) { return new Integer(x.value - y); }
    public static Integer operator -(Integer x, UInt64 y) { return new Integer(x.value - y); }
    public static Integer operator *(Integer x, Int16 y) { return new Integer(x.value * y); }
    public static Integer operator *(Integer x, Int32 y) { return new Integer(x.value * y); }
    public static Integer operator *(Integer x, Int64 y) { return new Integer(x.value * y); }
    public static Integer operator *(Integer x, UInt16 y) { return new Integer(x.value * y); }
    public static Integer operator *(Integer x, UInt32 y) { return new Integer(x.value * y); }
    public static Integer operator *(Integer x, UInt64 y) { return new Integer(x.value * y); }
    public static Integer operator /(Integer x, Int16 y) { return new Integer(x.value / y); }
    public static Integer operator /(Integer x, Int32 y) { return new Integer(x.value / y); }
    public static Integer operator /(Integer x, Int64 y) { return new Integer(x.value / y); }
    public static Integer operator /(Integer x, UInt16 y) { return new Integer(x.value / y); }
    public static Integer operator /(Integer x, UInt32 y) { return new Integer(x.value / y); }
    public static Integer operator /(Integer x, UInt64 y) { return new Integer(x.value / y); }
    public static Integer operator %(Integer x, Int16 y) { return new Integer(x.value % y); }
    public static Integer operator %(Integer x, Int32 y) { return new Integer(x.value % y); }
    public static Integer operator %(Integer x, Int64 y) { return new Integer(x.value % y); }
    public static Integer operator %(Integer x, UInt16 y) { return new Integer(x.value % y); }
    public static Integer operator %(Integer x, UInt32 y) { return new Integer(x.value % y); }
    public static Integer operator %(Integer x, UInt64 y) { return new Integer(x.value % y); }
    public static Integer operator +(Integer x, Integer y) { return new Integer(x.value + y.value); }
    public static Integer operator -(Integer x, Integer y) { return new Integer(x.value - y.value); }
    public static Integer operator *(Integer x, Integer y) { return new Integer(x.value * y.value); }
    public static Integer operator /(Integer x, Integer y) { return new Integer(x.value / y.value); }
    public static Integer operator %(Integer x, Integer y) { return new Integer(x.value % y.value); }
    public static bool operator ==(Integer x, Int16 y) { return x.value == y; }
    public static bool operator !=(Integer x, Int16 y) { return x.value != y; }
    public static bool operator ==(Integer x, Int32 y) { return x.value == y; }
    public static bool operator !=(Integer x, Int32 y) { return x.value != y; }
    public static bool operator ==(Integer x, Int64 y) { return x.value == y; }
    public static bool operator !=(Integer x, Int64 y) { return x.value != y; }
    public static bool operator ==(Integer x, UInt16 y) { return x.value == y; }
    public static bool operator !=(Integer x, UInt16 y) { return x.value != y; }
    public static bool operator ==(Integer x, UInt32 y) { return x.value == y; }
    public static bool operator !=(Integer x, UInt32 y) { return x.value != y; }
    public static bool operator ==(Integer x, UInt64 y) { return x.value == y; }
    public static bool operator !=(Integer x, UInt64 y) { return x.value != y; }
    public static bool operator ==(Integer x, Integer y) { return x.value == y.value; }
    public static bool operator !=(Integer x, Integer y) { return x.value != y.value; }
    public override bool Equals(object obj) { return this == (Integer)obj; }
    public override int GetHashCode() { return this.value.GetHashCode(); }
    public override string ToString() { return this.value.ToString(); }
    public static bool operator >(Integer x, Int16 y) { return x.value > y; }
    public static bool operator <(Integer x, Int16 y) { return x.value < y; }
    public static bool operator >(Integer x, Int32 y) { return x.value > y; }
    public static bool operator <(Integer x, Int32 y) { return x.value < y; }
    public static bool operator >(Integer x, Int64 y) { return x.value > y; }
    public static bool operator <(Integer x, Int64 y) { return x.value < y; }
    public static bool operator >(Integer x, UInt16 y) { return x.value > y; }
    public static bool operator <(Integer x, UInt16 y) { return x.value < y; }
    public static bool operator >(Integer x, UInt32 y) { return x.value > y; }
    public static bool operator <(Integer x, UInt32 y) { return x.value < y; }
    public static bool operator >(Integer x, UInt64 y) { return x.value > y; }
    public static bool operator <(Integer x, UInt64 y) { return x.value < y; }
    public static bool operator >(Integer x, Integer y) { return x.value > y.value; }
    public static bool operator <(Integer x, Integer y) { return x.value < y.value; }
    public static bool operator >=(Integer x, Int16 y) { return x.value >= y; }
    public static bool operator <=(Integer x, Int16 y) { return x.value <= y; }
    public static bool operator >=(Integer x, Int32 y) { return x.value >= y; }
    public static bool operator <=(Integer x, Int32 y) { return x.value <= y; }
    public static bool operator >=(Integer x, Int64 y) { return x.value >= y; }
    public static bool operator <=(Integer x, Int64 y) { return x.value <= y; }
    public static bool operator >=(Integer x, UInt16 y) { return x.value >= y; }
    public static bool operator <=(Integer x, UInt16 y) { return x.value <= y; }
    public static bool operator >=(Integer x, UInt32 y) { return x.value >= y; }
    public static bool operator <=(Integer x, UInt32 y) { return x.value <= y; }
    public static bool operator >=(Integer x, UInt64 y) { return x.value >= y; }
    public static bool operator <=(Integer x, UInt64 y) { return x.value <= y; }
    public static bool operator >=(Integer x, Integer y) { return x.value >= y.value; }
    public static bool operator <=(Integer x, Integer y) { return x.value <= y.value; }
    public static Integer operator +(Int16 x, Integer y) { return new Integer(x + y.value); }
    public static Integer operator +(Int32 x, Integer y) { return new Integer(x + y.value); }
    public static Integer operator +(Int64 x, Integer y) { return new Integer(x + y.value); }
    public static Integer operator +(UInt16 x, Integer y) { return new Integer(x + y.value); }
    public static Integer operator +(UInt32 x, Integer y) { return new Integer(x + y.value); }
    public static Integer operator +(UInt64 x, Integer y) { return new Integer(x + y.value); }
    public static Integer operator -(Int16 x, Integer y) { return new Integer(x - y.value); }
    public static Integer operator -(Int32 x, Integer y) { return new Integer(x - y.value); }
    public static Integer operator -(Int64 x, Integer y) { return new Integer(x - y.value); }
    public static Integer operator -(UInt16 x, Integer y) { return new Integer(x - y.value); }
    public static Integer operator -(UInt32 x, Integer y) { return new Integer(x - y.value); }
    public static Integer operator -(UInt64 x, Integer y) { return new Integer(x - y.value); }
    public static Integer operator *(Int16 x, Integer y) { return new Integer(x * y.value); }
    public static Integer operator *(Int32 x, Integer y) { return new Integer(x * y.value); }
    public static Integer operator *(Int64 x, Integer y) { return new Integer(x * y.value); }
    public static Integer operator *(UInt16 x, Integer y) { return new Integer(x * y.value); }
    public static Integer operator *(UInt32 x, Integer y) { return new Integer(x * y.value); }
    public static Integer operator *(UInt64 x, Integer y) { return new Integer(x * y.value); }
    public static Integer operator /(Int16 x, Integer y) { return new Integer(x / y.value); }
    public static Integer operator /(Int32 x, Integer y) { return new Integer(x / y.value); }
    public static Integer operator /(Int64 x, Integer y) { return new Integer(x / y.value); }
    public static Integer operator /(UInt16 x, Integer y) { return new Integer(x / y.value); }
    public static Integer operator /(UInt32 x, Integer y) { return new Integer(x / y.value); }
    public static Integer operator /(UInt64 x, Integer y) { return new Integer(x / y.value); }
    public static Integer operator %(Int16 x, Integer y) { return new Integer(x % y.value); }
    public static Integer operator %(Int32 x, Integer y) { return new Integer(x % y.value); }
    public static Integer operator %(Int64 x, Integer y) { return new Integer(x % y.value); }
    public static Integer operator %(UInt16 x, Integer y) { return new Integer(x % y.value); }
    public static Integer operator %(UInt32 x, Integer y) { return new Integer(x % y.value); }
    public static Integer operator %(UInt64 x, Integer y) { return new Integer(x % y.value); }
    public static bool operator ==(Int16 x, Integer y) { return x == y.value; }
    public static bool operator !=(Int16 x, Integer y) { return x != y.value; }
    public static bool operator ==(Int32 x, Integer y) { return x == y.value; }
    public static bool operator !=(Int32 x, Integer y) { return x != y.value; }
    public static bool operator ==(Int64 x, Integer y) { return x == y.value; }
    public static bool operator !=(Int64 x, Integer y) { return x != y.value; }
    public static bool operator ==(UInt16 x, Integer y) { return x == y.value; }
    public static bool operator !=(UInt16 x, Integer y) { return x != y.value; }
    public static bool operator ==(UInt32 x, Integer y) { return x == y.value; }
    public static bool operator !=(UInt32 x, Integer y) { return x != y.value; }
    public static bool operator ==(UInt64 x, Integer y) { return x == y.value; }
    public static bool operator !=(UInt64 x, Integer y) { return x != y.value; }
    public static bool operator >(Int16 x, Integer y) { return x > y.value; }
    public static bool operator <(Int16 x, Integer y) { return x < y.value; }
    public static bool operator >(Int32 x, Integer y) { return x > y.value; }
    public static bool operator <(Int32 x, Integer y) { return x < y.value; }
    public static bool operator >(Int64 x, Integer y) { return x > y.value; }
    public static bool operator <(Int64 x, Integer y) { return x < y.value; }
    public static bool operator >(UInt16 x, Integer y) { return x > y.value; }
    public static bool operator <(UInt16 x, Integer y) { return x < y.value; }
    public static bool operator >(UInt32 x, Integer y) { return x > y.value; }
    public static bool operator <(UInt32 x, Integer y) { return x < y.value; }
    public static bool operator >(UInt64 x, Integer y) { return x > y.value; }
    public static bool operator <(UInt64 x, Integer y) { return x < y.value; }
    public static bool operator >=(Int16 x, Integer y) { return x >= y.value; }
    public static bool operator <=(Int16 x, Integer y) { return x <= y.value; }
    public static bool operator >=(Int32 x, Integer y) { return x >= y.value; }
    public static bool operator <=(Int32 x, Integer y) { return x <= y.value; }
    public static bool operator >=(Int64 x, Integer y) { return x >= y.value; }
    public static bool operator <=(Int64 x, Integer y) { return x <= y.value; }
    public static bool operator >=(UInt16 x, Integer y) { return x >= y.value; }
    public static bool operator <=(UInt16 x, Integer y) { return x <= y.value; }
    public static bool operator >=(UInt32 x, Integer y) { return x >= y.value; }
    public static bool operator <=(UInt32 x, Integer y) { return x <= y.value; }
    public static bool operator >=(UInt64 x, Integer y) { return x >= y.value; }
    public static bool operator <=(UInt64 x, Integer y) { return x <= y.value; }
}
public static class Program
{
    private static bool IntegerFunction(Integer n)
    {
        //code that implements IntegerFunction goes here
        //note that there is NO code that checks the type of n in rum time, because it is NOT needed anymore 
    }
    private static void Main()
    {
        Console.WriteLine("{0}",IntegerFunction(0)); //compile error: there is no overloaded METHOD for objects of type "int" and no implicit conversion from any object, including "int", to "Integer" is known.
        Console.WriteLine("{0}",IntegerFunction(new Integer(0))); //both compiles and no run time error
        Console.WriteLine("{0}",IntegerFunction("string")); //compile error: there is no overloaded METHOD for objects of type "string" and no implicit conversion from any object, including "string", to "Integer" is known.
        Console.WriteLine("{0}",IntegerFunction(new Integer("string"))); //compile error: there is no overloaded CONSTRUCTOR for objects of type "string"
    }
}

Обратите внимание, что для использования динамического кода вы должны добавить ссылку на Microsoft.CSharp

Если версия .NET framework ниже / меньше / меньше 4.0, а динамическая неопределенная в этой версии, тогда вам придется использовать объект вместо этого и делать кастинг для целочисленного типа, что является проблемой, поэтому я рекомендую вам используйте хотя бы .NET 4.0 или новее, если вы можете так использовать динамический объект вместо объекта.

1
ответ дан Farewell Stack Exchange 25 August 2018 в 12:08
поделиться

Вероятно, самое близкое, что вы можете сделать, это

static bool IntegerFunction<T>(T value) where T: struct

Не уверен, что вы могли бы сделать следующее

static bool IntegerFunction<T>(T value) where T: struct, IComparable
, IFormattable, IConvertible, IComparable<T>, IEquatable<T>

Для чего-то такого конкретного, почему бы просто не перегружать для каждого тип, список настолько короткий и, возможно, меньше памяти.

10
ответ дан Haacked 25 August 2018 в 12:08
поделиться

Для этого нет ограничений. Это реальная проблема для тех, кто хочет использовать generics для числовых вычислений.

Я бы пошел дальше и сказал, что нам нужно

static bool GenericFunction<T>(T value) 
    where T : operators( +, -, /, * )

Или даже

static bool GenericFunction<T>(T value) 
    where T : Add, Subtract

К сожалению, у вас есть только интерфейсы, базовые классы и ключевые слова struct (должен быть тип значения), class (должен быть ссылочным типом) и new() (должен иметь конструктор по умолчанию)

Вы можете обернуть число в чем-то еще (аналогично INullable<T>), например здесь, в codeproject .


Вы можете применить ограничение во время выполнения (путем отражения для операторов или проверка типов), но это действительно лишает преимущество наличия общего в первую очередь.

75
ответ дан Keith 25 August 2018 в 12:08
поделиться

К сожалению, вы можете указать только конструкцию в предложении where в этом экземпляре. Кажется странным, что вы не можете указать Int16, Int32 и т. Д., Но я уверен, что существует некоторая глубокая причина внедрения, лежащая в основе решения не допускать типы значений в предложении where.

Я предполагаю, что единственное решение должно выполнить проверку времени выполнения, которая, к сожалению, предотвращает проблему, возникающую во время компиляции. Это было бы похоже на: -

static bool IntegerFunction<T>(T value) where T : struct {
  if (typeof(T) != typeof(Int16)  &&
      typeof(T) != typeof(Int32)  &&
      typeof(T) != typeof(Int64)  &&
      typeof(T) != typeof(UInt16) &&
      typeof(T) != typeof(UInt32) &&
      typeof(T) != typeof(UInt64)) {
    throw new ArgumentException(
      string.Format("Type '{0}' is not valid.", typeof(T).ToString()));
  }

  // Rest of code...
}

Который немного уродлив, я знаю, но по крайней мере предоставляет требуемые ограничения.

Я также посмотрел бы на возможную производительность последствия для этой реализации, возможно, есть более быстрый выход.

15
ответ дан ljs 25 August 2018 в 12:08
поделиться

Я бы использовал общий, который вы могли бы обрабатывать внешним ...

/// <summary>
/// Generic object copy of the same type
/// </summary>
/// <typeparam name="T">The type of object to copy</typeparam>
/// <param name="ObjectSource">The source object to copy</param>
public T CopyObject<T>(T ObjectSource)
{
    T NewObject = System.Activator.CreateInstance<T>();

    foreach (PropertyInfo p in ObjectSource.GetType().GetProperties())
        NewObject.GetType().GetProperty(p.Name).SetValue(NewObject, p.GetValue(ObjectSource, null), null);

    return NewObject;
}
1
ответ дан Marc Roussel 25 August 2018 в 12:08
поделиться

Обходной путь с использованием политик:

interface INumericPolicy<T>
{
    T Zero();
    T Add(T a, T b);
    // add more functions here, such as multiplication etc.
}

struct NumericPolicies:
    INumericPolicy<int>,
    INumericPolicy<long>
    // add more INumericPolicy<> for different numeric types.
{
    int INumericPolicy<int>.Zero() { return 0; }
    long INumericPolicy<long>.Zero() { return 0; }
    int INumericPolicy<int>.Add(int a, int b) { return a + b; }
    long INumericPolicy<long>.Add(long a, long b) { return a + b; }
    // implement all functions from INumericPolicy<> interfaces.

    public static NumericPolicies Instance = new NumericPolicies();
}

Алгоритмы:

static class Algorithms
{
    public static T Sum<P, T>(this P p, params T[] a)
        where P: INumericPolicy<T>
    {
        var r = p.Zero();
        foreach(var i in a)
        {
            r = p.Add(r, i);
        }
        return r;
    }

}

Использование:

int i = NumericPolicies.Instance.Sum(1, 2, 3, 4, 5);
long l = NumericPolicies.Instance.Sum(1L, 2, 3, 4, 5);
NumericPolicies.Instance.Sum("www", "") // compile-time error.

Решение безопасно для компиляции. CityLizard Framework предоставляет скомпилированную версию для .NET 4.0. Файл lib / NETFramework4.0 / CityLizard.Policy.dll.

Он также доступен в Nuget: https://www.nuget.org/packages/CityLizard/ . См. Структуру CityLizard.Policy.I.

80
ответ дан Mark Amery 25 August 2018 в 12:08
поделиться

Мне было интересно, как samjudson, почему только целые числа? и если это так, вам может понадобиться создать вспомогательный класс или что-то подобное для хранения всех типов, которые вы хотите.

Если все, что вам нужно, это целые числа, не используйте общий, то есть не общий; или еще лучше, отклоните любой другой тип, проверив его тип.

2
ответ дан Martin Marconcini 25 August 2018 в 12:08
поделиться

Я создал небольшую библиотечную функциональность для решения этих проблем:

Вместо:

public T DifficultCalculation<T>(T a, T b)
{
    T result = a * b + a; // <== WILL NOT COMPILE!
    return result;
}
Console.WriteLine(DifficultCalculation(2, 3)); // Should result in 8.

Вы могли бы написать:

public T DifficultCalculation<T>(Number<T> a, Number<T> b)
{
    Number<T> result = a * b + a;
    return (T)result;
}
Console.WriteLine(DifficultCalculation(2, 3)); // Results in 8.

Вы можете найти исходный код здесь: https://codereview.stackexchange.com/questions/26022/improvement-requested-for-generic-calculator-and-generic-number

3
ответ дан Martin Mulder 25 August 2018 в 12:08
поделиться

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

Мне хотелось что-то вроде

public struct Foo<T>
{
    public T Value{ get; private set; }

    public static Foo<T> operator +(Foo<T> LHS, Foo<T> RHS)
    {
        return new Foo<T> { Value = LHS.Value + RHS.Value; };
    }
}

Я работал над этой проблемой с использованием динамического ввода времени .net4.

public struct Foo<T>
{
    public T Value { get; private set; }

    public static Foo<T> operator +(Foo<T> LHS, Foo<T> RHS)
    {
        return new Foo<T> { Value = LHS.Value + (dynamic)RHS.Value };
    }
}

Две вещи об использовании dynamic:

  1. Производительность. Все типы значений получаются в коробке.
  2. Ошибки времени выполнения. Вы «били» компилятор, но теряете безопасность. Если общий тип не имеет определенного оператора, во время выполнения будет выбрано исключение.
1
ответ дан pomeroy 25 August 2018 в 12:08
поделиться

Невозможно ограничить типы шаблонов, но вы можете определить разные действия на основе типа. В качестве части общего числового пакета мне понадобился общий класс для добавления двух значений.

    class Something<TCell>
    {
        internal static TCell Sum(TCell first, TCell second)
        {
            if (typeof(TCell) == typeof(int))
                return (TCell)((object)(((int)((object)first)) + ((int)((object)second))));

            if (typeof(TCell) == typeof(double))
                return (TCell)((object)(((double)((object)first)) + ((double)((object)second))));

            return second;
        }
    }

Обратите внимание, что типыof оцениваются во время компиляции, поэтому операторы if будут удалены компилятором. Компилятор также удаляет ложные нажатия. Итак, что-то разрешило в компиляторе

        internal static int Sum(int first, int second)
        {
            return first + second;
        }
4
ответ дан Rob Deary 25 August 2018 в 12:08
поделиться

Не существует единого интерфейса или базового класса, который все они наследуют (который также не унаследован другими классами), поэтому простой ответ - нет.

Я действительно удивляюсь, почему это проблема. Что вы хотите сделать в своем классе IntegerFunction, который может быть сделан только для целых чисел?

-5
ответ дан samjudson 25 August 2018 в 12:08
поделиться

Типичные примитивные типы .NET не имеют общего интерфейса, который позволяет использовать их для вычислений. Можно было бы определить ваши собственные интерфейсы (например, ISignedWholeNumber), которые будут выполнять такие операции, определять структуры, которые содержат один Int16, Int32 и т. Д., И реализовывать эти интерфейсы, а затем использовать методы, которые принимают общие типы ограничено ISignedWholeNumber, но для преобразования числовых значений в типы структуры, вероятно, будет неприятность.

Альтернативным подходом было бы определить статический класс Int64Converter<T> со статическим свойством bool Available {get;}; и статическим делегаты для Int64 GetInt64(T value), T FromInt64(Int64 value), bool TryStoreInt64(Int64 value, ref T dest). Конструктор класса может быть жестко закодирован для загрузки делегатов для известных типов и, возможно, использовать Reflection для проверки того, реализует ли тип T методы с собственными именами и сигнатурами (в случае, если это что-то вроде структуры, которая содержит Int64 и представляет собой число, но имеет собственный метод ToString()). Такой подход потеряет преимущества, связанные с проверкой типа времени компиляции, но все равно удастся избежать операций в боксе, и каждый тип нужно будет только «проверять» один раз. После этого операции, связанные с этим типом, будут заменены диспетчером делегатов.

1
ответ дан supercat 25 August 2018 в 12:08
поделиться

Я думаю, вы недопонимаете дженерики. Если операция, которую вы пытаетесь выполнить, хороша только для определенных типов данных, тогда вы не выполняете что-то «generic».

Кроме того, поскольку вы хотите, чтобы функция работала только с типами данных int вам не нужна отдельная функция для каждого конкретного размера. Простое использование параметра в самом большом конкретном типе позволит программе автоматически подгонять к нему более мелкие типы данных. (т. е. передача Int16 будет автоматически конвертировать в Int64 при вызове).

Если вы выполняете разные операции на основе фактического размера int, передаваемого в функцию, тогда я думаю, вам следует серьезно пересмотреть даже попытку делать то, что вы делаете. Если вам нужно обмануть язык, вы должны думать немного больше о том, чего вы пытаетесь выполнить, а не о том, как делать то, что вы хотите.

В противном случае можно использовать параметр типа Object, а затем вам нужно будет проверить тип параметра и предпринять соответствующие действия или выбросить исключение.

-9
ответ дан Tom Welch 25 August 2018 в 12:08
поделиться
Другие вопросы по тегам:

Похожие вопросы: