Pergunta

Alguém pode me dizer se existe uma maneira de os genéricos limitarem um argumento de tipo genérico T apenas para:

  • Int16
  • Int32
  • Int64
  • UInt16
  • UInt32
  • UInt64

Estou ciente do where palavra-chave, mas não consegue encontrar uma interface para apenas esses tipos,

Algo como:

static bool IntegerFunction<T>(T value) where T : INumeric 
Foi útil?

Solução

C# não suporta isso.Hejlsberg descreveu os motivos para não implementar o recurso em uma entrevista com Bruce Eckel:

E não está claro se a complexidade adicional compensa o pequeno rendimento obtido.Se algo que você deseja fazer não for suportado diretamente no sistema de restrições, você poderá fazê-lo com um padrão de fábrica.Você poderia ter um Matrix<T>, por exemplo, e nisso Matrix você gostaria de definir um método de produto escalar.É claro que isso significa que você precisa entender como multiplicar dois Ts, mas você não pode dizer isso como uma restrição, pelo menos não se T é int, double, ou float.Mas o que você poderia fazer é ter o seu Matrix tome como argumento um Calculator<T>, e em Calculator<T>, tenha um método chamado multiply.Você vai implementar isso e passa para o Matrix.

No entanto, isso leva a um código bastante complicado, onde o usuário precisa fornecer seus próprios Calculator<T> implementação, para cada T que eles querem usar.Contanto que não precise ser extensível, ou seja,se você deseja apenas oferecer suporte a um número fixo de tipos, como int e double, você pode usar uma interface relativamente simples:

var mat = new Matrix<int>(w, h);

(Implementação mínima em um GitHub Gist.)

Entretanto, assim que você quiser que o usuário possa fornecer seus próprios tipos personalizados, será necessário abrir essa implementação para que o usuário possa fornecer seus próprios tipos. Calculator instâncias.Por exemplo, para instanciar uma matriz que usa uma implementação personalizada de ponto flutuante decimal, DFP, você teria que escrever este código:

var mat = new Matrix<DFP>(DfpCalculator.Instance, w, h);

… e implementar todos os membros para DfpCalculator : ICalculator<DFP>.

Uma alternativa, que infelizmente partilha as mesmas limitações, é trabalhar com classes de políticas, conforme discutido na resposta de Sergey Shandar.

Outras dicas

Considerando a popularidade desta questão e o interesse por trás de tal função, estou surpreso ao ver que ainda não há resposta envolvendo T4.

Neste código de exemplo, demonstrarei um exemplo muito simples de como você pode usar o poderoso mecanismo de modelagem para fazer o que o compilador faz nos bastidores com genéricos.

Em vez de se esforçar e sacrificar a certeza do tempo de compilação, você pode simplesmente gerar a função desejada para cada tipo que desejar e usá-la adequadamente (em tempo de compilação!).

Para fazer isso:

  • Crie um novo Modelo de texto arquivo chamado GenericNumberMethodTemplate.tt.
  • Remova o código gerado automaticamente (você manterá a maior parte dele, mas parte não será necessária).
  • Adicione o seguinte trecho:
<#@ template language="C#" #>
<#@ output extension=".cs" #>
<#@ assembly name="System.Core" #>

<# Type[] types = new[] {
    typeof(Int16), typeof(Int32), typeof(Int64),
    typeof(UInt16), typeof(UInt32), typeof(UInt64)
    };
#>

using System;
public static class MaxMath {
    <# foreach (var type in types) { 
    #>
        public static <#= type.Name #> Max (<#= type.Name #> val1, <#= type.Name #> val2) {
            return val1 > val2 ? val1 : val2;
        }
    <#
    } #>
}

É isso.Você terminou agora.

Salvar este arquivo irá compilá-lo automaticamente neste arquivo de origem:

using System;
public static class MaxMath {
    public static Int16 Max (Int16 val1, Int16 val2) {
        return val1 > val2 ? val1 : val2;
    }
    public static Int32 Max (Int32 val1, Int32 val2) {
        return val1 > val2 ? val1 : val2;
    }
    public static Int64 Max (Int64 val1, Int64 val2) {
        return val1 > val2 ? val1 : val2;
    }
    public static UInt16 Max (UInt16 val1, UInt16 val2) {
        return val1 > val2 ? val1 : val2;
    }
    public static UInt32 Max (UInt32 val1, UInt32 val2) {
        return val1 > val2 ? val1 : val2;
    }
    public static UInt64 Max (UInt64 val1, UInt64 val2) {
        return val1 > val2 ? val1 : val2;
    }
}

Na tua main método você pode verificar se tem certeza em tempo de compilação:

namespace TTTTTest
{
    class Program
    {
        static void Main(string[] args)
        {
            long val1 = 5L;
            long val2 = 10L;
            Console.WriteLine(MaxMath.Max(val1, val2));
            Console.Read();
        }
    }
}

enter image description here

Vou adiantar uma observação:não, isso não é uma violação do princípio DRY.O princípio DRY existe para evitar que as pessoas dupliquem código em vários locais, o que dificultaria a manutenção do aplicativo.

Este não é o caso aqui:se você quiser uma mudança, basta alterar o modelo (uma única fonte para toda a sua geração!) e pronto.

Para usá-lo com suas próprias definições personalizadas, adicione uma declaração de namespace (certifique-se de que seja a mesma onde você definirá sua própria implementação) ao código gerado e marque a classe como partial.Depois, adicione estas linhas ao seu arquivo de modelo para que ele seja incluído na eventual compilação:

<#@ import namespace="TheNameSpaceYouWillUse" #>
<#@ assembly name="$(TargetPath)" #>

Sejamos honestos:Isso é bem legal.

Isenção de responsabilidade:esta amostra foi fortemente influenciada por Metaprogramação em .NET por Kevin Hazzard e Jason Bock, Manning Publications.

Não há restrição para isso.É um problema real para quem deseja usar genéricos para cálculos numéricos.

Eu iria mais longe e diria que precisamos

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

Ou mesmo

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

Infelizmente você só tem interfaces, classes base e palavras-chave struct (deve ser do tipo valor), class (deve ser do tipo de referência) e new() (deve ter construtor padrão)

Você poderia agrupar o número em outra coisa (semelhante a INullable<T>) como aqui no codeproject.


Você poderia aplicar a restrição em tempo de execução (refletindo para os operadores ou verificando os tipos), mas isso perde a vantagem de ter o genérico em primeiro lugar.

Solução alternativa usando políticas:

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();
}

Algoritmos:

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;
    }

}

Uso:

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.

A solução é segura em tempo de compilação. Estrutura CityLizard fornece versão compilada para .NET 4.0.O arquivo é lib/NETFramework4.0/CityLizard.Policy.dll.

Também está disponível no Nuget: https://www.nuget.org/packages/CityLizard/.Ver CityLizard.Policy.I estrutura.

Esta questão é um pouco do tipo FAQ, então estou postando isso como wiki (já que já postei algo semelhante antes, mas este é mais antigo);de qualquer forma...

Qual versão do .NET você está usando?Se você estiver usando o .NET 3.5, então eu tenho um implementação de operadores genéricos em MiscUtil (grátis, etc.).

Isso tem métodos como T Add<T>(T x, T y), e outras variantes para aritmética em diferentes tipos (como DateTime + TimeSpan).

Além disso, isso funciona para todos os operadores integrados, elevados e personalizados e armazena em cache o delegado para desempenho.

Algumas informações adicionais sobre por que isso é complicado são aqui.

Você também pode querer saber disso dynamic (4.0) também resolve esse problema indiretamente - ou seja,

dynamic x = ..., y = ...
dynamic result = x + y; // does what you expect

Infelizmente, você só pode especificar struct na cláusula where neste caso.Parece estranho que você não possa especificar Int16, Int32, etc.especificamente, mas tenho certeza de que há algum motivo profundo de implementação subjacente à decisão de não permitir tipos de valor em uma cláusula where.

Acho que a única solução é fazer uma verificação em tempo de execução, o que infelizmente evita que o problema seja detectado em tempo de compilação.Isso seria algo como: -

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...
}

O que é um pouco feio, eu sei, mas pelo menos fornece as restrições necessárias.

Eu também analisaria possíveis implicações de desempenho para esta implementação, talvez haja uma maneira mais rápida.

Provavelmente o mais próximo que você pode fazer é

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

Não tenho certeza se você poderia fazer o seguinte

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

Para algo tão específico, por que não ter apenas sobrecargas para cada tipo, a lista é tão curta e possivelmente ocuparia menos memória.

Não há como restringir os modelos aos tipos, mas você pode definir diferentes ações com base no tipo.Como parte de um pacote numérico genérico, eu precisava de uma classe genérica para adicionar dois valores.

    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;
        }
    }

Observe que os typeofs são avaliados em tempo de compilação, portanto as instruções if seriam removidas pelo compilador.O compilador também remove conversões falsas.Então, algo resolveria no compilador para

        internal static int Sum(int first, int second)
        {
            return first + second;
        }

Criei uma pequena funcionalidade de biblioteca para resolver estes problemas:

Em vez de:

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.

Você poderia escrever:

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.

Você pode encontrar o código fonte aqui: https://codereview.stackexchange.com/questions/26022/improvement-requested-for-generic-calculator-and-generic-number

Fiquei me perguntando o mesmo que Samjudson, por que apenas para números inteiros?e se for esse o caso, você pode criar uma classe auxiliar ou algo parecido para armazenar todos os tipos desejados.

Se tudo que você quer são números inteiros, não use genérico, isso não é genérico;ou melhor ainda, rejeite qualquer outro tipo verificando seu tipo.

Qual é o objetivo do exercício?

Como as pessoas já apontaram, você poderia ter uma função não genérica que pegasse o item maior, e o compilador converteria automaticamente entradas menores para você.

static bool IntegerFunction(Int64 value) { }

Se a sua função estiver no caminho crítico para o desempenho (muito improvável, IMO), você poderá fornecer sobrecargas para todas as funções necessárias.

static bool IntegerFunction(Int64 value) { }
...
static bool IntegerFunction(Int16 value) { }

Eu usaria um genérico que você pudesse manipular externamente ...

/// <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;
}

Essa limitação me afetou quando tentei sobrecarregar operadores para tipos genéricos;como não havia restrição "INumeric" e por uma série de outras razões que as boas pessoas do stackoverflow têm prazer em fornecer, as operações não podem ser definidas em tipos genéricos.

eu queria algo parecido

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; };
    }
}

Eu resolvi esse problema usando a digitação dinâmica em tempo de execução .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 };
    }
}

As duas coisas sobre usar dynamic são

  1. Desempenho.Todos os tipos de valor ficam em caixa.
  2. Erros de tempo de execução.Você "venceu" o compilador, mas perdeu a segurança do tipo.Se o tipo genérico não tiver o operador definido, uma exceção será lançada durante a execução.

Ainda não existe uma solução “boa” para isso.No entanto, você pode restringir significativamente o argumento de tipo para descartar muitos desajustes para sua hipotética restrição 'INumeric', como Haacked mostrou acima.

static bool IntegerFunction<T>(valor T) onde T:ICOMPARABLE, IFORMATCABLE, ICONVERTIBLE, ICOMPARABLEu003CT> , Iequatableu003CT> , struct {...

Os tipos primitivos numéricos do .NET não compartilham nenhuma interface comum que permita seu uso para cálculos.Seria possível definir suas próprias interfaces (por exemplo, ISignedWholeNumber) que realizaria tais operações, define estruturas que contêm um único Int16, Int32, etc.e implementar essas interfaces e, em seguida, ter métodos que aceitem tipos genéricos restritos a ISignedWholeNumber, mas ter que converter valores numéricos em seus tipos de estrutura provavelmente seria um incômodo.

Uma abordagem alternativa seria definir uma classe estática Int64Converter<T> com uma propriedade estática bool Available {get;}; e delegados estáticos para Int64 GetInt64(T value), T FromInt64(Int64 value), bool TryStoreInt64(Int64 value, ref T dest).O construtor da classe poderia ser codificado para carregar delegados para tipos conhecidos e possivelmente usar Reflection para testar se o tipo T implementa métodos com nomes e assinaturas próprios (caso seja algo como uma estrutura que contém um Int64 e representa um número, mas tem um costume ToString() método).Esta abordagem perderia as vantagens associadas à verificação de tipo em tempo de compilação, mas ainda conseguiria evitar operações de boxe e cada tipo teria que ser "verificado" apenas uma vez.Depois disso, as operações associadas a esse tipo seriam substituídas por um despacho delegado.

Se você estiver usando o .NET 4.0 e posterior, basta usar dinâmico como argumento do método e verifique em tempo de execução que o passado dinâmico o tipo de argumento é do tipo numérico/inteiro.

Se o tipo de passado dinâmico é não tipo numérico/inteiro e lança exceção.

Um exemplo curto o código que implementa a ideia é algo como:

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.
    }

É claro que esta solução funciona apenas em tempo de execução, mas nunca em tempo de compilação.

Se você deseja uma solução que sempre funcione em tempo de compilação e nunca em tempo de execução, você terá que agrupar o dinâmico com uma estrutura/classe pública cujo sobrecarregado público os construtores aceitam argumentos apenas dos tipos desejados e fornecem o nome apropriado à estrutura/classe.

Faz sentido que o embrulhado dinâmico é sempre privado membro da classe/estrutura e é o único membro da estrutura/classe e o nome do único membro da estrutura/classe é "valor".

Você também terá que definir e implementar público métodos e/ou operadores que funcionam com os tipos desejados para o membro dinâmico privado da classe/estrutura, se necessário.

Também faz sentido que a estrutura/classe tenha especial/único construtor que aceita dinâmico como argumento que inicializa, é apenas um membro dinâmico privado chamado "valor", mas o modificador deste construtor é privado claro.

Assim que a classe/estrutura estiver pronta, defina o tipo de IntegerFunction do argumento como aquela classe/estrutura que foi definida.

Um exemplo longo o código que implementa a ideia é algo como:

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"
    }
}

Observe que para usar dinâmico no seu código você deve Adicionar referência para Microsoft.CSharp

Se a versão do .NET framework for inferior/inferior/inferior a 4.0 e dinâmico é indefinido nessa versão, então você terá que usar objeto em vez disso, faça a conversão para o tipo inteiro, o que é um problema, então recomendo que você use pelo menos .NET 4.0 ou mais recente, se puder, para poder usar dinâmico em vez de objeto.

Se tudo que você quer é usar um tipo numérico, você poderia considerar criar algo semelhante a um alias em C++ com using.

Então, em vez de ter o genérico

T ComputeSomething<T>(T value1, T value2) where T : INumeric { ... }

você pode ter

using MyNumType = System.Double;
T ComputeSomething<MyNumType>(MyNumType value1, MyNumType value2) { ... }

Isso pode permitir que você passe facilmente de double para int ou outros, se necessário, mas você não conseguiria usar ComputeSomething com double e int no mesmo programa.

Mas por que não substituir todos double para int então?Como seu método pode querer usar um double se a entrada é double ou int.O alias permite saber exatamente qual variável usa o dinâmico tipo.

Tive uma situação semelhante em que precisei lidar com tipos numéricos e strings;parece uma mistura um pouco bizarra, mas pronto.

Novamente, como muitas pessoas, observei as restrições e criei um monte de interfaces que elas deveriam suportar.No entanto, a) não era 100% estanque eb), qualquer pessoa que olhasse para esta longa lista de restrições ficaria imediatamente muito confusa.

Então, minha abordagem foi colocar toda a minha lógica em um método genérico sem restrições, mas tornar esse método genérico privado.Em seguida, expus-o com métodos públicos, um deles manipulando explicitamente o tipo que eu queria manipular - na minha opinião, o código é limpo e explícito, por exemplo.

public static string DoSomething(this int input, ...) => DoSomethingHelper(input, ...);
public static string DoSomething(this decimal input, ...) => DoSomethingHelper(input, ...);
public static string DoSomething(this double input, ...) => DoSomethingHelper(input, ...);
public static string DoSomething(this string input, ...) => DoSomethingHelper(input, ...);

private static string DoSomethingHelper<T>(this T input, ....)
{
    // complex logic
}

Não existe uma interface ou classe base única que todos herdem (que também não seja herdada por outras classes), portanto a resposta simples é não.

Eu me pergunto por que isso é um problema.O que você deseja fazer dentro da sua classe IntegerFunction que só pode ser feito com números inteiros?

Acho que você está entendendo mal os genéricos.Se a operação que você está tentando executar for válida apenas para tipos de dados específicos, você não estará fazendo algo "genérico".

Além disso, como você deseja permitir que a função funcione apenas em tipos de dados int, não será necessária uma função separada para cada tamanho específico.Simplesmente usar um parâmetro no maior tipo específico permitirá que o programa faça upcast automaticamente dos tipos de dados menores para ele.(ou seja,passar um Int16 será convertido automaticamente para Int64 ao chamar).

Se você estiver executando operações diferentes com base no tamanho real do int que está sendo passado para a função, acho que você deveria reconsiderar seriamente, mesmo tentando fazer o que está fazendo.Se você tiver que enganar a linguagem, você deve pensar um pouco mais sobre o que está tentando realizar, em vez de como fazer o que deseja.

Caso contrário, um parâmetro do tipo Object poderá ser usado e então você terá que verificar o tipo do parâmetro e tomar as medidas apropriadas ou lançar uma exceção.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top