Verificarea tipului general

Is there a way to enforce/limit the types that are passed to primitives? (bool, int, string, etc.)

Acum, știu că puteți limita parametrul de tip generic la o implementare de tip sau de interfață prin intermediul clauzei unde . Cu toate acestea, acest lucru nu se potrivește facturii pentru primitive (AFAIK), deoarece nu toate au un teren comun (cu excepția obiectului înainte de cineva spune!: P).

Deci, gândurile mele actuale sunt doar să-mi sparg dinții și să fac o instrucțiune comutator și să arunc o eroare ArgumentException la eșec.


EDIT 1:

Doar pentru a clarifica:

Definiția codului ar trebui să fie:

public class MyClass ....

Și instanțierea:

MyClass = new MyClass();//Legal
MyClass = new MyClass();//Legal
MyClass = new MyClass();//Illegal
MyClass = new MyClass();//Illegal (but looks awesome!)

EDIT 2

@Jon Limjap - Punct bun, și ceva ce mă gândeam deja .. Sunt sigur că există o metodă generică care poate fi utilizată pentru a determina dacă tipul este de valoare sau tip de referință.

Acest lucru ar putea fi util pentru a elimina instantaneu o mulțime de obiecte pe care nu vreau să le tratez (dar atunci trebuie să vă faceți griji cu privire la structurile care sunt utilizate, cum ar fi Dimensiune ) .. Problema interesantă nu? :)

Aici este:

where T : struct

Luat de la MSDN .


Sunt curios .. Ar putea fi făcut în. NET 3.x folosind metode de extensie? Creați o interfață și implementați interfața în metodele de extensie (care ar fi probabil mai puțin curat decât un comutator de grăsime bit). În plus, dacă mai trebuie să extindeți mai târziu la oricare tip personalizat ușor, aceștia pot implementa aceeași interfață, fără modificări necesare pentru codul de bază.

Voi ce credeți?

Vestea trista este ca lucrez in cadrul 2! : D


EDIT 3

Acest lucru a fost atât de simplu ca urmare a Pointerul lui Jon Limjaps .. Atât de simplu vreau aproape să plâng, dar este minunat deoarece codul funcționează ca un farmec!

Deci, iată ce am făcut (veți râde!):

Codul a fost adăugat clasei generice

bool TypeValid()
{
   //Get the TypeCode from the Primitive Type
    TypeCode code = Type.GetTypeCode(typeof(PrimitiveDataType));

   //All of the TypeCode Enumeration refer Primitive Types
   //with the exception of Object and Empty (Null).
   //Since I am willing to allow Null Types (at this time)
   //all we need to check for is Object!
    switch (code)
    {
        case TypeCode.Object:
            return false;
        default:
            return true;
    }
}

Apoi o mică metodă de utilitate pentru a verifica tipul și a arunca o excepție,

private void EnforcePrimitiveType()
{
    if (!TypeValid())
        throw new InvalidOperationException(
            "Unable to Instantiate SimpleMetadata based on the Generic Type of '" + typeof(PrimitiveDataType).Name + 
            "' - this Class is Designed to Work with Primitive Data Types Only.");
}

Tot ce trebuie făcut este să apelați EnforcePrimitiveType() în constructorii de clase. Treaba făcuta! :-)

Singurul dezavantaj, aruncă doar o excepție la timpul de execuție (evident), mai degrabă decât timpul de design .. Dar asta nu este mare lucru și ar putea fi luat cu utilități ca FxCop (pe care nu o folosim la serviciu).

Mulțumiri speciale lui Jon Limjap pe aceasta!

0
fr hi bn
De asemenea, ați putea apela verificarea în constructorul static, așa că se numește o singură dată pe tip utilizat ca argument generic.
adăugat autor Julien, sursa

8 răspunsuri

Primitivele par să fie specificate în TypeCode enumerare:

Poate că există o modalitate de a afla dacă un obiect conține codul TypeCode enum fără a fi nevoie să îl deplasați la un anumit obiect sau să apelați GetType() sau typeof() ?

Update It was right under my nose. The code sample there shows this:

static void WriteObjectInfo(object testObject)
{
    TypeCode    typeCode = Type.GetTypeCode( testObject.GetType() );

    switch( typeCode )
    {
        case TypeCode.Boolean:
            Console.WriteLine("Boolean: {0}", testObject);
            break;

        case TypeCode.Double:
            Console.WriteLine("Double: {0}", testObject);
            break;

        default:
            Console.WriteLine("{0}: {1}", typeCode.ToString(), testObject);
            break;
        }
    }
}

E încă un comutator urât. Dar este un loc bun pentru a începe!

0
adăugat
Ei bine, nu știu cum, ci un punct principal de discuție între obiecte și primive este că cele mai multe primitive sunt de fapt structurate, nu clase. Voi căuta o modalitate de a le descoperi programat :)
adăugat autor Jon Limjap, sursa
> clasa publica Class1 where> GenericType: struct {}>> Aceasta pare sa faca treaba. Problema pe care o vad cu acea implementare este ca daca ai un struct custom va veni inca ca un primitiv, care nu este cazul.
adăugat autor Jon Limjap, sursa
Bun punct, și ceva ce am fost deja în considerare .. Sunt sigur că există o metodă generică care poate fi folosit pentru a determina dacă tipul este de o valoare sau de tip de referință .. Acest lucru ar putea fi util în eliminarea instantanee o mulțime de obiecte Nu vreau să mă ocup de (dar atunci trebuie să vă faceți griji cu privire la structurile care sunt utilizate, cum ar fi Dimensiune ) .. Problema interesantă nu? :) EDIT: Ah da, aici este: unde T: struct Luat de la MSDN .
adăugat autor Rob Cooper, sursa
Sunt curios .. Mă întreb dacă acest lucru ar putea fi făcut în. NET 3.x folosind metode de extensie .. Creați o interfață .. Implementați interfața în metodele de extindere .. (care ar fi, probabil, mai curat decât un pic de grăsime comutator) .. În plus, dacă mai trebuie să extindeți ulterior la orice tip personalizat ușor, aceștia pot implementa aceeași interfață, fără modificări necesare pentru codul de bază. Voi ce credeți? Vestea trista este ca lucrez in cadrul 2! : D EDIT: Lăsând biroul în 5 minute, o să o iau când mă întorc acasă! : D
adăugat autor Rob Cooper, sursa

Destul de mult ce a spus @Lars:

//Force T to be a value (primitive) type.
public class Class1 where T: struct

//Force T to be a reference type.
public class Class1 where T: class

//Force T to be a parameterless constructor.
public class Class1 where T: new()

Toate lucrările în .NET 2, 3 și 3.5.

0
adăugat
public class Class1 where GenericType : struct
{
}

Aceasta părea să facă treaba.

0
adăugat

Dacă puteți tolera utilizarea metodelor din fabrică (în locul constructorilor MyClass pe care i-ați cerut), puteți face întotdeauna ceva de genul:

class MyClass
{
  private readonly T _value;

  private MyClass(T value) { _value = value; }

  public static MyClass FromInt32(int value) { return new MyClass(value); }
  public static MyClass FromString(string value) { return new MyClass(value); }
 //etc for all the primitive types, or whatever other fixed set of types you are concerned about
}

A problem here is that you would need to type MyClass.FromInt32, which is annoying. There isn't a very good way around this if you want to maintain the private-ness of the constructor, but here are a couple of workarounds:

  • Create an abstract class MyClass. Make MyClass inherit from MyClass and nest it within MyClass. Move the static methods to MyClass. This will all the visibility work out, at the cost of having to access MyClass as MyClass.MyClass.
  • Use MyClass as given. Make a static class MyClass which calls the static methods in MyClass using MyClass (probably using the appropriate type each time, just for giggles).
  • (Easier, but certainly weird) Make an abstract type MyClass which inherits from MyClass. (For concreteness, let's say MyClass.) Because you can call static methods defined in a base class through the name of a derived class, you can now use MyClass.FromString.

Acest lucru vă oferă o verificare statică în detrimentul mai multor scrisori.

Dacă sunteți mulțumit de verificarea dinamică, aș folosi câteva variante ale soluției TypeCode de mai sus.

0
adăugat

Puteți simplifica metoda EnforcePrimitiveType utilizând typeof (PrimitiveDataType) .IsPrimitive . Am pierdut ceva?

0
adăugat
Trecerea unui șir în acea instrucțiune returnează False, iar posterul specifica că are nevoie de șir pentru a reveni la True. .NET consideră că char este un element primitiv, dar nu șir.
adăugat autor Nicholas, sursa

Use a custom FxCop rule that flags undesirable usage of MyClass<>.

0
adăugat

@Rob, Enum va aluneca prin funcția TypeValid , deoarece TypeCode este Integer . Am actualizat funcția de verificat și pentru Enum .

Private Function TypeValid() As Boolean
    Dim g As Type = GetType(T)
    Dim code As TypeCode = Type.GetTypeCode(g)

    ' All of the TypeCode Enumeration refer Primitive Types
    ' with the exception of Object and Empty (Nothing).
    ' Note: must also catch Enum as its type is Integer.
    Select Case code
        Case TypeCode.Object
            Return False
        Case Else
            ' Enum's TypeCode is Integer, so check BaseType
            If g.BaseType Is GetType(System.Enum) Then
                Return False
            Else
                Return True
            End If
    End Select
End Function
0
adăugat

Având o provocare similară, mă întrebam cum v-ați simțit despre Interfața IConvertible . Aceasta permite ceea ce solicită solicitantul și vă puteți extinde cu propriile implementări.

Exemplu:

    public class MyClass
    where TKey : IConvertible
{
   //class intentionally abbreviated
}

Mă gândesc la asta ca la o soluție, cu toate că multe dintre cele sugerate făceau parte și din selecția mea.

Preocuparea mea este - cu toate acestea - este înșelătoare pentru dezvoltatorii potențiali care folosesc clasa ta?

Noroc - și mulțumiri.

0
adăugat