2010-12-07 45 views
4

Dans .net, il est possible d'utiliser des génériques de telle sorte qu'une fonction peut accepter des arguments qui supportent une ou plusieurs interfaces et dérivent d'un type de base, même s'il n'existe aucun type unique dont dérivent tous les types d'arguments valides. Par exemple, on pourrait dire:Stockage d'un objet qui met en œuvre plusieurs interfaces et dérive d'une certaine base (.net)

Sub Foo(Of T As {IInterface1, IInterface2, SomeBaseType})(Param as T) 

et être autorisés à passer tout dérivé de SomeBaseType qui met en œuvre à la fois IInterface1 et IInterface2. Cela fonctionnera même si SomeBaseType ne supporte pas Interface1 et Interface2, et les classes qui implémentent ces interfaces ne partagent aucun ancêtre commun qui les implémente aussi.

Cela peut être très pratique si l'on aura pas besoin de garder le paramètre partout après que la fonction a quitté. Malheureusement, je n'arrive pas à trouver un moyen de conserver le paramètre transmis de telle manière qu'il puisse ensuite être passé à une fonction similaire, sauf peut-être en utilisant Reflection. Est-ce qu'il y a une bonne façon de faire ça?

Le plus proche que je suis en mesure de trouver est de définir une interface INEST (peut-être pas le meilleur nom - tout le monde peut l'améliorer?) Ainsi:

Interface INest(Of Out T) 
    Function Nest() As T 
End Interface 

Et pour toute interface qui sera être utilisé en combinaison avec d'autres ou avec la classe de base « contrainte », définir une version générique comme illustré ci-dessous

Interface IFun1 
    ' Any members of the interface go here, e.g. ...' 
    Sub DoFun1() 
End Interface 

Interface IFun1(Of Out T) 
    ' This one does nothing but inherit' 
    Inherits IFun1, INest(Of T) 
End Interface 

une classe qui soutiendra plusieurs interfaces devraient se déclarer mettre en œuvre les génériques, avec lui-même comme le type argument.

Class test123a 
    Inherits sampleBase 
    Implements IFun1(Of test123a), IFun2(Of test123a), IFun3(Of test123a) 
End Class 

Si cela est fait, on peut définir un argument de fonction ou variable de classe qui prend en charge plusieurs contraintes ainsi:

Dim SomeField as IFun1(Of IFun2(Of IFun3(Of sampleBase))) 

puis lui attribuer une classe dérivée de sampleBase, qui implémente ces interfaces. SomeField implémentera IFun1; SomeField.Nest implémentera IFun2; SomeField.Nest.Nest implémentera IFun3. Notez qu'il n'est pas nécessaire que IFun1, IFun2, IFun3 ou sampleBase partagent une dérivation commune autre que les interfaces génériques héritées de INest (Of T). Notez également que, quel que soit le nombre d'interfaces dérivées par INest implémentées par une classe, il suffit de définir une implémentation de INest (Of T) .Nest. Pas exactement beau, mais il y a deux choses intéressantes: (1) toute classe concrète qui implémente en fait les interfaces nécessaires peut être assignée directement à un champ déclaré comme ci-dessus, sans un typecast; (2) alors que les champs qui enchaînent les types dans un ordre différent ne sont pas compatibles avec l'assignation, ils peuvent être mutuellement transposés.

Y at-il une meilleure façon de stocker quelque chose de telle manière qu'il est « connu » pour supporter de multiples interfaces et tirer à partir d'un certain type de base? Etant donné que l'on peut écrire un tel code de manière sécurisée, il semblerait que le .net 2.0 CLR puisse probablement supporter une telle chose si les compilateurs offrent un peu d'aide. Cependant, je ne suis pas au courant d'une approche particulièrement intéressante avec les compilateurs actuels.

Répondre

2

La meilleure façon que je peux penser est de faire un stockage abstrait et la mise en œuvre générique de ce stockage. Par exemple (excusez mon VB.NET):

MustInherit Class Storage 
    Public MustOverride Sub DoSomething() 
End Class 

Class Storage(Of T As {IInterface1, IInterface2, SomeBaseType}) 
    Inherits Storage 

    Public Overrides Sub DoSomething() 
     ' do something with Value. 
    End Sub 

    Public Value As T 
End Class 

et l'utilisation

Dim S As Storage 

Sub Foo(Of T As {IInterface1, IInterface2, SomeBaseType})(ByVal Param As T) 
    S = New Storage(Of T) With {.Value = Param} 
End Sub 

Sub UseS() 
    S.DoSomething(); 
End Sub 

Mise à jour: Ok, parce que nous ne pouvons pas être en mesure d'identifier à l'avance toutes les actions:

MustInherit Class Storage 
    MustOverride ReadOnly Property SomeBaseType As SomeBaseType 
    MustOverride ReadOnly Property IInterface1 As IInterface1 
    MustOverride ReadOnly Property IInterface2 As IInterface2 
End Class 

Class Storage(Of T As {IInterface1, IInterface2, SomeBaseType}) 
    Inherits Storage 

    Public Value As T 

    Public Overrides ReadOnly Property IInterface1 As IInterface1 
     Get 
      Return Value 
     End Get 
    End Property 

    Public Overrides ReadOnly Property IInterface2 As IInterface2 
     Get 
      Return Value 
     End Get 
    End Property 

    Public Overrides ReadOnly Property SomeBaseType As SomeBaseType 
     Get 
      Return Value 
     End Get 
    End Property 
End Class 
+1

qui fonctionne si l'on peut identifier à l'avance toutes les actions que l'on pourrait vouloir faire avec un objet. Peut-être y aurait-il une façon pas trop horrible de l'utiliser avec des classes partielles pour que des routines do-something différentes puissent être définies près des endroits où elles étaient invoquées, mais le code finirait par être excessivement verbeux. Il y a des moments où le préprocesseur C est mauvais, mais il y a des moments où il peut certainement aider à gérer la verbosité. – supercat

+0

D'accord, qu'en est-il de la version mise à jour? –