Existe uma restrição que restringe o meu método genérico para tipos numéricos?

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

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

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

Algo como:

 static bool IntegerFunction(T value) where T : INumeric 

    Hejlsberg descreveu as razões para não implementar o recurso em uma entrevista com Bruce Eckel.

    Eu tenho que admitir, porém, que eu não sei como ele acha que a solução proposta funcionará. Sua proposta é adiar operações aritméticas para alguma outra class genérica (leia a entrevista!). Como isso ajuda? IMHO, não muito.

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

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

    Em vez de passar por aros e sacrificar a certeza em tempo de compilation, você pode simplesmente gerar a function que deseja para cada tipo que quiser e usá-la de acordo (em tempo de compilation!).

    Para fazer isso:

    • Crie um novo arquivo de modelo de texto chamado GenericNumberMethodTemplate.tt .
    • Remova o código gerado automaticamente (você manterá a maior parte, mas alguns não são necessários).
    • 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 aí. Você está feito agora.

    Salvar este arquivo irá compilá-lo automaticamente para este 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; } } 

    No seu método main , você pode verificar se possui certeza de tempo de compilation:

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

    insira a descrição da imagem aqui

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

    Isto não é de todo o caso aqui: se você quer uma mudança, então você pode simplesmente mudar o template (uma fonte única 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 é a mesma que você definirá sua própria implementação) ao seu código gerado e marque a class como partial . Depois, adicione essas linhas ao seu arquivo de modelo para que ele seja incluído na eventual compilation:

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

    Sejamos honestos: isso é bem legal.

    Disclaimer: este exemplo foi fortemente influenciado por Metaprogramming in .NET por Kevin Hazzard e Jason Bock, Manning Publications .

    Não há restrições para isso. É um problema real para qualquer pessoa que queira usar genéricos para cálculos numéricos.

    Eu iria mais longe e diria que precisamos

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

    Ou mesmo

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

    Infelizmente você só tem interfaces, classs base e as palavras-chave struct (deve ser do tipo-valor), class (deve ser do tipo reference) e new() (deve ter o construtor default)

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


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

    Solução alternativa usando políticas:

     interface INumericPolicy { T Zero(); T Add(T a, T b); // add more functions here, such as multiplication etc. } struct NumericPolicies: INumericPolicy, INumericPolicy // add more INumericPolicy<> for different numeric types. { int INumericPolicy.Zero() { return 0; } long INumericPolicy.Zero() { return 0; } int INumericPolicy.Add(int a, int b) { return a + b; } long INumericPolicy.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

    (this P p, params T[] a) where P: INumericPolicy { 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 compilation. O CityLizard Framework fornece uma versão compilada para o .NET 4.0. O arquivo é lib / NETFramework4.0 / CityLizard.Policy.dll.

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

    Esta pergunta é um pouco de FAQ, então estou postando isso como wiki (já que eu postei similar antes, mas esta é uma mais antiga); de qualquer forma…

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

    Isso tem methods como T Add(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 internos, aprimorados e sob medida, e armazena em cache o delegado para desempenho.

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

    Você também pode querer saber que o tipo dynamic (4.0) resolve esse problema indiretamente também – isto é,

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

    Infelizmente você só é capaz de especificar struct na cláusula where desta instância. Parece estranho que você não possa especificar Int16, Int32, etc. especificamente, mas tenho certeza de que há uma profunda razão de implementação subjacente à decisão de não permitir tipos de valor em uma cláusula where.

    Eu acho que a única solução é fazer uma verificação de tempo de execução que, infelizmente, impede que o problema seja detectado em tempo de compilation. Isso seria algo como: –

     static bool IntegerFunction(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... } 

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

    Eu também verifico as possíveis implicações de desempenho para essa implementação, talvez haja uma maneira mais rápida de fazer isso.

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

     static bool IntegerFunction(T value) where T: struct 

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

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

    Para algo tão específico, por que não apenas ter sobrecargas para cada tipo, a lista é tão curta e possivelmente teria menos espaço de memory.

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

      class Something { 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 compilation, portanto, as instruções if serão removidas pelo compilador. O compilador também remove lançamentos espúrios. Então, algo resolveria no compilador para

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

    Eu criei uma pequena funcionalidade de biblioteca para resolver esses problemas:

    Ao invés de:

     public T DifficultCalculation(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(Number a, Number b) { Number 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

    Eu estava pensando o mesmo que samjudson, porque apenas para inteiros? e se for esse o caso, você pode querer criar uma class auxiliar ou algo assim para manter todos os tipos que você deseja.

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

    Qual é o objective do exercício?

    Como as pessoas já apontaram, você poderia ter uma function não genérica levando o maior item, e o compilador automaticamente converterá ints menores para você.

     static bool IntegerFunction(Int64 value) { } 

    Se sua function estiver no caminho crítico de 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ê poderia manipular externamente …

     ///  /// Generic object copy of the same type ///  /// The type of object to copy /// The source object to copy public T CopyObject(T ObjectSource) { T NewObject = System.Activator.CreateInstance(); 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; já que não havia restrição “Numérica” ​​e, por várias outras razões, as pessoas boas em stackoverflow estão dispostas a fornecer, as operações não podem ser definidas em tipos genéricos.

    Eu queria algo parecido

     public struct Foo { public T Value{ get; private set; } public static Foo operator +(Foo LHS, Foo RHS) { return new Foo { Value = LHS.Value + RHS.Value; }; } } 

    Eu tenho trabalhado em torno deste problema usando .net4 dinâmica de tempo de execução de digitação.

     public struct Foo { public T Value { get; private set; } public static Foo operator +(Foo LHS, Foo RHS) { return new Foo { Value = LHS.Value + (dynamic)RHS.Value }; } } 

    As duas coisas sobre o uso dynamic são

    1. Atuação. Todos os tipos de valor são colocados em checkbox.
    2. Erros de tempo de execução. Você “vence” o compilador, mas perde 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.

    Não há uma solução “boa” para isso ainda. No entanto, você pode restringir o argumento do tipo de forma significativa para descartar muitos missfits para sua hipotética restrição “Numérica”, como Haacked mostrou acima.

    bool estático IntegerFunction (valor T) onde T: IComparable, IFormattable, IConvertible, IComparable , IEquatable , struct {…

    Os tipos primitivos numéricos do .NET não compartilham nenhuma interface comum que permita que eles sejam usados ​​para cálculos. Seria possível definir suas próprias interfaces (por exemplo, ISignedWholeNumber ) que executariam tais operações, definir estruturas que contivessem um único Int16 , Int32 , etc. e implementar essas interfaces, e então ter methods que aceitam tipos genéricos restritos a ISignedWholeNumber , mas tendo converter valores numéricos em seus tipos de estrutura provavelmente seria um incômodo.

    Uma abordagem alternativa seria definir a class estática Int64Converter com uma propriedade estática bool Available {get;}; e estáticos delegates para Int64 GetInt64(T value) , T FromInt64(Int64 value) , bool TryStoreInt64(Int64 value, ref T dest) . O construtor de class pode ser codificado para carregar delegates para tipos conhecidos e possivelmente usar o Reflection para testar se o tipo T implementa methods com os nomes e assinaturas apropriados (caso seja algo como uma estrutura que contenha um Int64 e represente um número, mas tem um método ToString() personalizado). Essa abordagem perderia as vantagens associadas à verificação de tipo em tempo de compilation, mas ainda conseguiria evitar as operações de boxe e cada tipo só precisaria ser “verificado” uma vez. Depois disso, as operações associadas a esse tipo seriam substituídas por um despacho de delegado.

    Se você estiver usando o .NET 4.0 e posterior, poderá usar apenas o argumento dynamic como método e verificar em tempo de execução se o tipo de argumento dynamic transmitido é do tipo numérico / inteiro.

    Se o tipo da dinâmica passada não for numérico / inteiro, ative a exceção.

    Um exemplo de código curto 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 essa solução funciona somente em tempo de execução, mas nunca em tempo de compilation.

    Se você quiser uma solução que sempre funcione em tempo de compilation e nunca em tempo de execução, terá que agrupar a dinâmica com uma struct / class pública cujos construtores públicos sobrecarregados aceitam apenas argumentos dos tipos desejados e fornecem o nome apropriado struct / class.

    Faz sentido que a dinâmica envolvida seja sempre membro particular da class / struct e seja o único membro da class struct e que o nome do único membro da class struct seja “value”.

    Você também terá que definir e implementar methods públicos e / ou operadores que trabalhem com os tipos desejados para o membro dynamic particular da class / struct, se necessário.

    Também faz sentido que o struct / class tenha um construtor especial / exclusivo que aceite dynamic como argumento que inicializa seu único membro dynamic chamado “value”, mas o modificador deste construtor é privado, é claro.

    Quando a class / struct estiver pronta, defina o tipo de IntegerFunction do argumento como aquela class / estrutura que foi definida.

    Um exemplo de código longo 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" } } 

    Note that in order to use dynamic in your code you must Add Reference to Microsoft.CSharp

    If the version of the .NET framework is below/under/lesser than 4.0 and dynamic is undefined in that version then you will have to use object instead and do casting to the integer type, which is trouble, so I recommend that you use at least .NET 4.0 or newer if you can so you can use dynamic instead of object .

    There is no single interface or base class that they all inherit (that is not also inherited by other classs) so the simple answer is no.

    I do wonder why this is an issue though. What are you wanting to do inside your IntegerFunction class that can only be done to integers?

    I think you are misunderstanding generics. If the operation you are trying to perform is only good for specific data types then you are not doing something “generic”.

    Also, since you are only wanting to allow the function to work on int data types then you shouldn’t need a separate function for each specific size. Simply taking a parameter in the largest specific type will allow the program to automatically upcast the smaller data types to it. (ie passing an Int16 will auto-convert to Int64 when calling).

    If you are performing different operations based on the actual size of int being passed into the function then I would think you should seriously reconsider even trying to do what you are doing. If you have to fool the language you should think a bit more about what you are trying to accomplish rather than how to do what you want.

    Failing all else, a parameter of type Object could be used and then you will have to check the type of the parameter and take appropriate action or throw an exception.