2008-10-10 32 views
69

¿Es posible definir una clase en C# tal queC# genéricos no permitirá Delegado Tipo Restricciones

class GenericCollection<T> : SomeBaseCollection<T> where T : Delegate 

que podría no para la vida de mí realizar esta última noche en .NET 3.5. He intentado utilizar

delegate, Delegate, Action<T> and Func<T, T>

Me parece que esto debería ser permitido de alguna manera. Estoy tratando de implementar mi propio EventQueue.

Terminé simplemente haciendo esto [aproximación primitiva que te importa].

internal delegate void DWork(); 

class EventQueue { 
    private Queue<DWork> eventq; 
} 

Pero luego pierdo la capacidad de reutilizar la misma definición para diferentes tipos de funciones.

¿Pensamientos?

Respuesta

64

Una serie de clases no están disponibles como restricciones genéricas - Enum es otro.

Para los delegados, el más cercano que puedes conseguir es ": clase", tal vez usando la reflexión para comprobar (por ejemplo, en el constructor estático) que el T es un delegado:

static GenericCollection() 
{ 
    if (!typeof(T).IsSubclassOf(typeof(Delegate))) 
    { 
     throw new InvalidOperationException(typeof(T).Name + " is not a delegate type"); 
    } 
} 
+7

+1 por: 1) usando el constructor estático y 2) incluyendo un mensaje detallado debido a condiciones de depuración extrañas que rodean la inicialización de tipo. –

+6

@MarcGravell: No arrojar una excepción en un inicializador estático viola 'CA1065: no levantar excepciones en ubicaciones inesperadas' ... siempre asumí que debes usar una regla de análisis de código personalizado para encontrar usos no válidos de tu clase que normalmente no están disponibles en tiempo de ejecución. –

13

Editar : Algunas soluciones temporales propuestas se proponen en estos artículos:

http://jacobcarpenters.blogspot.com/2006/06/c-30-and-delegate-conversion.html

http://jacobcarpenters.blogspot.com/2006_11_01_archive.html


Desde el C# 2.0 specification podemos leer (20.7, Restricciones):

A-tipo de clase de restricción deben satisfacer las siguientes reglas:

  • El tipo debe ser un tipo de clase.
  • El tipo no debe sellarse.
  • El tipo no debe ser uno de los siguientes tipos: System.Array, System.Delegate, System.Enum o System.ValueType.
  • El tipo no debe ser objeto. Como todos los tipos se derivan del objeto, tal restricción no tendría ningún efecto si estuviera permitida.
  • Como máximo, una restricción para un parámetro de tipo determinado puede ser un tipo de clase.

Y, por supuesto VS2008 escupe un error:

error CS0702: Constraint cannot be special class 'System.Delegate' 

Para información e investigación sobre este tema, lea here.

3

Delegate ya admite el encadenamiento. ¿Esto no satisface tus necesidades?

public class EventQueueTests 
{ 
    public void Test1() 
    { 
     Action myAction =() => Console.WriteLine("foo"); 
     myAction +=() => Console.WriteLine("bar"); 

     myAction(); 
     //foo 
     //bar 
    } 

    public void Test2() 
    { 
     Action<int> myAction = x => Console.WriteLine("foo {0}", x); 
     myAction += x => Console.WriteLine("bar {0}", x); 
     myAction(3); 
     //foo 3 
     //bar 3 
    } 

    public void Test3() 
    { 
     Func<int, int> myFunc = x => { Console.WriteLine("foo {0}", x); return x + 2; }; 
     myFunc += x => { Console.WriteLine("bar {0}", x); return x + 1; }; 
     int y = myFunc(3); 
     Console.WriteLine(y); 

     //foo 3 
     //bar 3 
     //4 
    } 

    public void Test4() 
    { 
     Func<int, int> myFunc = x => { Console.WriteLine("foo {0}", x); return x + 2; }; 
     Func<int, int> myNextFunc = x => { x = myFunc(x); Console.WriteLine("bar {0}", x); return x + 1; }; 
     int y = myNextFunc(3); 
     Console.WriteLine(y); 

     //foo 3 
     //bar 5 
     //6 
    } 

} 
+0

esa no es realmente la funcionalidad que estoy buscando ... Estaba tratando de hacer una restricción de tipo en mi clase genérica ... –

3

me encontré con una situación en la que tenía que hacer frente a una Delegate internamente pero quería una limitación genérica. Específicamente, quería agregar un controlador de eventos utilizando la reflexión, pero quería usar un argumento genérico para el delegado. El código siguiente no funciona, ya que "Handler" es una variable de tipo, y el compilador no desechará Handler-Delegate:

public void AddHandler<Handler>(Control c, string eventName, Handler d) { 
    c.GetType().GetEvent(eventName).AddEventHandler(c, (Delegate) d); 
} 

Sin embargo, puede pasar una función que hace la conversión para usted. convert toma un argumento y devuelve un HandlerDelegate:

public void AddHandler<Handler>(Control c, string eventName, 
        Func<Delegate, Handler> convert, Handler d) { 
     c.GetType().GetEvent(eventName).AddEventHandler(c, convert(d)); 
} 

Ahora el compilador es feliz. Llamar al método es fácil. Por ejemplo, adjuntando al evento KeyPress en un control de Windows Forms:

AddHandler<KeyEventHandler>(someControl, 
      "KeyPress", 
      (h) => (KeyEventHandler) h, 
      SomeControl_KeyPress); 

donde SomeControl_KeyPress es el destino del evento. La clave es el convertidor lambda: no funciona, pero convence al compilador de que le otorgó un delegado válido.

(Begin 280Z28) @Justin: ¿Por qué no utilizar esto?

public void AddHandler<Handler>(Control c, string eventName, Handler d) { 
    c.GetType().GetEvent(eventName).AddEventHandler(c, d as Delegate); 
} 

(Fin 280Z28)

+1

@Justin: Edité su respuesta para poner mi comentario al final ya que tiene una bloque de código –

+1

Buena captura. Eso funciona muy bien. –

10

Si usted está dispuesto a tomar una dependencia del tiempo de compilación en un IL Tejedor se puede hacer esto con Fody.

El uso de este complemento a Fody https://github.com/Fody/ExtraConstraints

El código puede tener este aspecto

public class Sample 
{ 
    public void MethodWithDelegateConstraint<[DelegateConstraint] T>() 
    {   
    } 
    public void MethodWithEnumConstraint<[EnumConstraint] T>() 
    { 
    } 
} 

Y compilarse a este

public class Sample 
{ 
    public void MethodWithDelegateConstraint<T>() where T: Delegate 
    { 
    } 

    public void MethodWithEnumConstraint<T>() where T: struct, Enum 
    { 
    } 
} 
+0

Enlace roto. ¿Tienes uno actual? –

+0

@JustinMorgan actualizado – Simon

2

Como se mencionó anteriormente, no se puede tener Delegados y Enum como una restricción genérica. System.Object y System.ValueType tampoco pueden usarse como una restricción genérica.

La solución puede ser si construyes una llamada apropiada en ti IL. Funcionará bien.

He aquí un buen ejemplo de Jon Skeet.

http://code.google.com/p/unconstrained-melody/

he llevado a mis referencias de Jon Skeet libro de C# en el Departamento edición 3.

1

Según MSDN

compilador CS0702 error

restricción no puede ser especial clase 'identificador' no puede ser usado como restricciones de los siguientes tipos:

  • sistema.Objeto
  • System.array
  • System.Delegate
  • System.Enum
  • System.ValueType.
Cuestiones relacionadas