Domanda

Qualcuno può dirmi se c'è un modo con i farmaci generici al limite di un argomento di tipo generico T solo:

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

Sono consapevole del where parola chiave, ma non riesco a trovare un'interfaccia per solo questi tipi di

Qualcosa di simile a:

static bool IntegerFunction<T>(T value) where T : INumeric 
È stato utile?

Soluzione

C# non supporta questa.Hejlsberg ha descritto le ragioni per non implementare la funzionalità in un'intervista con Bruce Eckel:

E non è chiaro che la complessità è la pena il piccolo rendimento che si ottiene.Se qualcosa che si vuole fare non è supportata direttamente nel vincolo di sistema, è possibile farlo con una fabbrica modello.Si potrebbe avere un Matrix<T>, per esempio, e in che Matrix per definire un prodotto di puntino metodo.Che, naturalmente, il che significa che è, in definitiva, è necessario capire come moltiplicare due Ts, ma non si può dire che come un vincolo, almeno non se T è int, double, o float.Ma quello che si può fare è di avere il vostro Matrix si prenda come argomento un Calculator<T>, e in Calculator<T>, hanno un metodo chiamato multiply.Si va implementare e si passa al Matrix.

Tuttavia, questo comporta abbastanza contorto codice, in cui l'utente deve fornire il proprio Calculator<T> attuazione, per ogni T che si desidera utilizzare.Fintanto che non deve essere estensibile, cioèse si desidera solo per supportare un numero fisso di tutti i tipi, come int e double, si può ottenere via con un'interfaccia relativamente semplice:

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

(Minimo attuazione in GitHub Gist.)

Tuttavia, non appena si desidera che l'utente sia in grado di fornire loro, tipi personalizzati, è necessario aprire questa implementazione, in modo che l'utente possa fornire il proprio Calculator le istanze.Per esempio, per creare un'istanza di una matrice che utilizza un custom decimale in virgola mobile attuazione, DFP, bisognerebbe scrivere questo codice:

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

... e realizzare tutti i membri per DfpCalculator : ICalculator<DFP>.

Un'alternativa, che, purtroppo, le azioni, le stesse limitazioni, è quello di lavorare con classi di criteri, come discusso in Sergey Shandar risposta.

Altri suggerimenti

Considerando la popolarità di questa domanda e l'interesse che sta dietro a tale funzione sono sorpreso di vedere che non c'è una risposta che coinvolge T4 di sicurezza.

In questo esempio di codice verrà illustrato un esempio molto semplice di come è possibile utilizzare il potente motore di template per fare quello che il compilatore praticamente dietro le quinte con i farmaci generici.

Invece di passare attraverso i cerchi e sacrificare in fase di compilazione certezza è possibile generare semplicemente la funzione che si desidera utilizzare per ogni tipo si amano e che, di conseguenza, (in fase di compilazione!).

Per fare questo:

  • Creare un nuovo Modello Di Testo file chiamato GenericNumberMethodTemplate.tt.
  • Rimuovere il codice generato automaticamente (ti tenere la maggior parte di esso, ma alcuni non è necessario).
  • Aggiungere il seguente frammento:
<#@ 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;
        }
    <#
    } #>
}

Che è.Hai fatto ora.

Salvare questo file verrà automaticamente compilare questo file di origine:

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

Nel main il metodo si può verificare che in fase di compilazione certezza:

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

Cercherò di ottenere di fronte a una considerazione:no, questa non è una violazione del LAVAGGIO principio.Il LAVAGGIO è il principio, per impedire la duplicazione di codice in più luoghi che potrebbero causare l'applicazione per diventare difficile da gestire.

Questo non è il caso qui:se si vuole un cambiamento, allora si può solo cambiare il modello (una singola fonte per tutte le generazioni!) ed è fatta.

Al fine di utilizzare, con le proprie definizioni personalizzate, aggiungi una dichiarazione dello spazio dei nomi (assicurarsi che sia lo stesso di quello in cui si definiscono la propria implementazione) per il codice generato e contrassegnare la classe come partial.Successivamente, aggiungere queste righe al file di template, quindi sarà incluso nell'eventuale compilazione:

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

Cerchiamo di essere onesti:Questo è piuttosto fresco.

Disclaimer:questo campione è stato fortemente influenzato da Metaprogrammazione in .NETTO da Kevin Hazzard e Jason Bock, Manning Publications.

Non c'è nessun vincolo per questo.E ' un vero problema per chi vuole utilizzare farmaci generici per i calcoli numerici.

Mi piacerebbe andare oltre e dire che abbiamo bisogno

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

O anche

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

Purtroppo hai solo interfacce, classi di base e le parole chiave struct (deve essere di tipo di valore), class (deve essere un tipo di riferimento) e new() (deve avere il costruttore di default)

Si può avvolgere il numero in qualcos'altro (simile a INullable<T>) come qui su codeproject.


Si potrebbe applicare la restrizione in fase di runtime (riflettendo per gli operatori e per il controllo per i tipi), ma che fa perdere il vantaggio di avere il generico, in primo luogo.

Soluzione utilizzando i criteri:

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

Algoritmi:

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

}

Utilizzo:

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.

La soluzione è in fase di compilazione cassetta di sicurezza. CityLizard Quadro fornisce una versione compilata per .NET 4.0.Il file è lib/NETFramework4.0/CityLizard.Policy.dll.

E ' disponibile anche in Nuget: https://www.nuget.org/packages/CityLizard/.Vedere CityLizard.Politica.Io struttura.

Questa domanda è un po ' una FAQ, così sto postando questo wiki (dato che ho postato di simile prima, ma questo è un vecchio);comunque...

Che versione di .NET stai usando?Se si utilizza .NET 3.5, quindi ho un generico operatori di attuazione in MiscUtil (libero, ecc).

Questo ha metodi come T Add<T>(T x, T y), e altre varianti per l'aritmetica su diversi tipi (come DateTime + TimeSpan).

Inoltre, questo funziona per tutte integrato, sollevato e su misura, gli operatori e le memorizza nella cache il delegato per le prestazioni.

Alcuni ulteriori informazioni sul motivo per cui questo è difficile è qui.

Si potrebbe anche voler sapere che dynamic (4.0) una sorta di risolve questo problema indirettamente troppo - cioè

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

Purtroppo sono solo in grado di specificare struct nella clausola where in questo caso.Sembra strano, non è possibile specificare Int16, Int32, etc.in particolare, ma sono sicuro che ci sono alcune profonde attuazione motivo alla base della decisione di non consentire i tipi di valore in una clausola where.

Credo che l'unica soluzione è fare un controllo di runtime che, purtroppo, il problema impedisce di essere preso a tempo di compilazione.Che piacerebbe andare qualcosa come:-

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

Che è un po ' brutto lo so, ma almeno fornisce i necessari vincoli.

Mi piacerebbe anche prendere in considerazione di possibili implicazioni in termini di prestazioni per questa implementazione, forse c'è un modo più veloce là fuori.

Probabilmente il più vicino si può fare è

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

Non sono sicuro se si può fare come segue

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

Per qualcosa di così specifico, perché non solo con i sovraccarichi per ciascun tipo, l'elenco è troppo breve e si sarebbe probabilmente meno ingombro di memoria.

Non c'è un modo per limitare l'modelli di tipi, ma si può definire azioni diverse a seconda del tipo.Come parte di un generico numerico pacchetto, avevo bisogno di una classe generica per aggiungere due valori.

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

Nota che il typeofs sono valutati in fase di compilazione, in modo che se il bilancio dovrebbe essere rimosso dal compilatore.Il compilatore rimuove anche spurie cast.Quindi Qualcosa potrebbe risolvere il compilatore

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

Ho creato una piccola biblioteca funzionalità per risolvere questi problemi:

Invece di:

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.

Si potrebbe scrivere:

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.

Potete trovare il codice sorgente qui: https://codereview.stackexchange.com/questions/26022/improvement-requested-for-generic-calculator-and-generic-number

Mi chiedevo la stessa samjudson, perché solo a numeri interi?e se questo è il caso, si potrebbe desiderare di creare una classe di supporto, o qualcosa di simile per contenere tutti i tipi che si desidera.

Se si desidera che i numeri interi, non utilizzare un generico, che non è una generica;o, meglio ancora, di rifiuto di qualsiasi altro tipo controllando il suo tipo.

Qual è il punto di questo esercizio?

Come persone hanno sottolineato già, si potrebbe avere una funzione generica di prendere la più grande voce, e il compilatore converte automaticamente i file più piccoli di int per voi.

static bool IntegerFunction(Int64 value) { }

Se la funzione e le prestazioni fondamentali del percorso (molto improbabile, IMO), è possibile fornire i sovraccarichi per tutte le funzioni necessarie.

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

Vorrei utilizzare uno generico che si potesse gestire externaly...

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

Questa limitazione mi ha colpito quando ho cercato di sovraccarico operatori per i tipi generici;dal momento che non c'era "INumeric" vincolo, e per una serie di altri motivi che le persone buone su stackoverflow sono felice di fornire, per le operazioni non possono essere definiti in tipi generici.

Volevo qualcosa di simile

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

Ho lavorato intorno a questo problema .net4 dinamica di battitura.

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

Le due cose sull'utilizzo di dynamic sono

  1. Prestazioni.Tutti i tipi di valore in scatola.
  2. Gli errori di Runtime.Si "beat", il compilatore, ma perdere il tipo di sicurezza.Se il tipo generico non ha l'operatore definito, verrà generata un'eccezione durante l'esecuzione.

Non c'e 'una buona soluzione per questo ancora.Tuttavia, è possibile restringere il tipo di argomento in modo significativo alla regola di molti missfits per il tuo ipotetico 'INumeric' vincolo di Haacked ha mostrato sopra.

static bool IntegerFunction<T>(Valore T) dove T:IComparable, IFormattable, IConvertible, IComparable<T>, IEquatable<T>struct {...

L' .NET numerici tipi primitivi di non condividere nessuna common interface che consente loro di essere utilizzati per i calcoli.Sarebbe possibile definire le proprie interfacce (ad es. ISignedWholeNumber) di cui vuoi eseguire tali operazioni, definire le strutture che contengono una singola Int16, Int32, etc.e l'attuazione di tali interfacce, e quindi sono metodi che accettano i tipi generici costretto a ISignedWholeNumber, ma dover convertire i valori numerici per i vostri tipi di struttura potrebbe essere un fastidio.

Un approccio alternativo sarebbe quello di definire la classe statica Int64Converter<T> con una proprietà statica bool Available {get;}; e statico delegati per Int64 GetInt64(T value), T FromInt64(Int64 value), bool TryStoreInt64(Int64 value, ref T dest).Il costruttore della classe potrebbe essere hard-coded per caricare i delegati per i tipi noti, e, eventualmente, utilizzare la Reflection per verificare se il tipo di T implementa i metodi con i nomi propri e le firme (nel caso in cui qualcosa di simile a una struct che contiene un Int64 e rappresenta un numero, ma è una consuetudine ToString() metodo).Questo approccio consentirebbe di perdere i vantaggi associati con la fase di compilazione tipo di controllo, ma sarebbe ancora in grado di evitare operazioni di boxing e ogni tipo di essere "controllato" una volta.Dopo di che, le operazioni connesse con che tipo potrebbe essere sostituito con un delegato della spedizione.

Se si utilizza .NET 4.0 e versioni successive, quindi si può semplicemente utilizzare dinamica come argomento del metodo e di controllo in fase di runtime che il passato dinamica l'argomento è di tipo numerico/tipo integer.

Se il tipo del passato dinamica è non numerico/tipo integer poi gettare l'eccezione.

Un esempio breve il codice che implementa l'idea è qualcosa di simile a:

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

Naturalmente che questa soluzione funziona in esecuzione solo, ma non in fase di compilazione.

Se si desidera una soluzione che funziona sempre in fase di compilazione e non in fase di esecuzione, allora devi avvolgere il dinamica con un pubblico struct/classe a cui overload pubblico costruttori di accettare argomenti, i tipi e solo dare la struttura/classe di nome appropriato.

Ha senso che il avvolta dinamica è sempre privato membro della classe/struttura ed è l'unico membro della struttura, la classe e il nome del membro della struttura/classe è "valore".

Si dovrà, inoltre, definire e attuare pubblico metodi e/o operatori che lavorano con i tipi per il privato dinamico membro della classe/struttura, se necessario.

Ha anche senso che la struttura/classe ha speciale/unico costruttore che accetta dinamica come argomento che inizializza è solo privata membro dinamico chiamato "valore", ma il modificatore di questo costruttore è privato di corso.

Una volta che la classe/struct è pronto a definire l'argomento del tipo di IntegerFunction da che classe/struttura che è stata definita.

Un esempio lungo il codice che implementa l'idea è qualcosa di simile a:

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

Si noti che per utilizzare dinamica nel codice si deve Aggiungi Riferimento per Microsoft.CSharp

Se la versione del .NET framework è sotto/sotto/di meno che la 4.0 e dinamica non è definita in che versione sarà necessario utilizzare oggetto invece e fare il casting per il tipo intero, che è il problema, così mi consiglia di utilizzare almeno .NET 4.0 o più recente, se possibile in modo che è possibile utilizzare dinamica invece di oggetto.

Se si desidera utilizzare un tipo numerico, si potrebbe prendere in considerazione la creazione di qualcosa di simile a un alias in C++ con using.

Così invece di avere molto generico

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

si potrebbe avere

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

Che consentono di passare facilmente da double per int o altri, se necessario, ma non essere in grado di utilizzare ComputeSomething con double e int nello stesso programma.

Ma perché non sostituire tutto double per int quindi?Perché il metodo consiglia l'utilizzo di un double se l'input è double o int.L'alias consente di sapere esattamente che utilizza la variabile dinamica tipo.

Ho avuto una situazione simile, in cui avevo bisogno per gestire i tipi numerici e stringhe;sembra un po ' bizzarra di un mix, ma ci si va.

Di nuovo, come molte persone ho guardato i vincoli e si avvicinò con una serie di interfacce che doveva sopportare.Tuttavia, a) non è al 100% a tenuta stagna e b), a chi sta guardando questo lungo elenco di vincoli sarebbe subito molto confuso.

Quindi, il mio approccio è stato quello di mettere tutti i miei logica in un metodo generico senza vincoli, ma per fare che il metodo generico privata.Ho poi esposto con metodi pubblici, esplicitamente gestione il tipo mi ha voluto gestire - per la mia mente, il codice è pulito ed esplicita, ad esempio

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
}

Non c'è una singola interfaccia o classe di base che ereditano tutti (che non è ereditato anche da altre classi, quindi la risposta è: no.

Mi chiedo perché questo è un problema però.Che cosa si vuole fare all'interno del vostro IntegerFunction classe che può essere fatto solo di numeri interi?

Penso che si sono incomprensione generics.Se l'operazione che si sta tentando di eseguire è buono solo per specifici tipi di dati, quindi non si fa qualcosa di "generico".

Inoltre, visto che sono solo a voler consentire la funzione di lavorare su tipi di dati int quindi non c'è bisogno di una funzione separata per ogni dimensione specifica.Semplicemente prendendo un parametro che è il più grande tipo specifico consentirà il programma automaticamente upcast i tipi di minori dimensioni ad esso.(cioèil passaggio di un Int16 auto-conversione in Int64 durante la chiamata).

Se siete di eseguire diverse operazioni, sulla base dell'effettiva dimensione dell'int di essere passati alla funzione, quindi, io penso che si dovrebbe seriamente riconsiderare anche cercando di fare quello che stai facendo.Se si dispone di ingannare la lingua che si dovrebbe pensare un po ' di più su ciò che si sta cercando di realizzare, piuttosto che come fare quello che vuoi.

In mancanza d'altro, un parametro di tipo Object può essere utilizzato e quindi sarà necessario verificare il tipo di parametro e prendere i provvedimenti opportuni o lanciare un'eccezione.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top