2012-05-31 20 views
88

Leyendo this, me enteré de que era posible permitir que un método aceptara parámetros de varios tipos haciéndolo un método genérico. En el ejemplo, el siguiente código se usa con una restricción de tipo para asegurar que "U" sea IEnumerable<T>.Método genérico múltiple (OR) tipo restricción

public T DoSomething<U, T>(U arg) where U : IEnumerable<T> 
{ 
    return arg.First(); 
} 

he encontrado algunas más código que permitió la adición de múltiples restricciones de tipo, tales como:

public void test<T>(string a, T arg) where T: ParentClass, ChildClass 
{ 
    //do something 
} 

Sin embargo, este código aparece para hacer cumplir esa arg debe ser a la vez un tipo de ParentClassyChildClass. Lo que quiero hacer es decir que arg podría ser un tipo de ParentClassoChildClass de la siguiente manera:

public void test<T>(string a, T arg) where T: string OR Exception 
{ 
//do something 
} 

Su ayuda se agradece como siempre!

+3

¿Qué podría hacer, * de forma genérica * en el cuerpo de dicho método (a menos que los múltiples tipos procedan de una clase base específica, en cuyo caso, ¿por qué no declararlo como la restricción de tipo)? –

+0

@Damien_The_Unbeliever ¿No estás seguro de a qué te refieres en el cuerpo? A menos que me refiera a permitir cualquier tipo y verificar manualmente el cuerpo ... y en el código real que quiero escribir (el último fragmento de código), me gustaría poder pasar una cadena O excepción, por lo que no hay clase relación allí (excepto el sistema.objeto que imagino). – Mansfield

+1

También tenga en cuenta que no sirve de nada escribir 'where T: string', como 'string' es una clase ** sealed **. Lo único útil que puedes hacer es definir sobrecargas para 'string' y' T: Exception', como explica @ Botz3000 en su respuesta a continuación. –

Respuesta

44

Eso no es posible. Puede, sin embargo, definir las sobrecargas para tipos específicos:

public void test(string a, string arg); 
public void test(string a, Exception arg); 

Si esas son parte de una clase genérica, serán preferibles a la versión genérica del método.

+1

Interesante. Esto se me ocurrió, pero pensé que podría hacer que el código sea más limpio para usar una función. Ah, bueno, ¡muchas gracias! Solo por curiosidad, ¿sabes si hay una razón específica por la que esto no es posible? (¿Ha sido dejado fuera del lenguaje deliberadamente?) – Mansfield

+2

@Mansfield No conozco la razón exacta, pero creo que ya no podrá usar los parámetros genéricos de una manera significativa. Dentro de la clase, tendrían que ser tratados como objeto si se les permitiera ser de tipos completamente diferentes. Eso significa que también podría omitir el parámetro genérico y proporcionar sobrecargas. – Botz3000

+2

@Mansfield, es porque una relación 'o' hace que las cosas lejanas a lo general sean útiles. Deberías * tener * una reflexión para descubrir qué hacer y todo eso. (¡YUCK!). – Crisfole

5

Si ChildClass significa que se deriva de ParentClass, puede escribir lo siguiente para aceptar tanto ParentClass y ChildClass;

public void test<T>(string a, T arg) where T: ParentClass 
{ 
    //do something 
} 

En el otherhand, si desea utilizar dos tipos diferentes, sin relación de herencia entre ellos, se debe considerar el tipo de aplicación de la misma interfaz;

public interface ICommonInterface 
{ 
    string SomeCommonProperty { get; set; } 
} 

public class AA : ICommonInterface 
{ 
    public string SomeCommonProperty 
    { 
     get;set; 
    } 
} 

public class BB : ICommonInterface 
{ 
    public string SomeCommonProperty 
    { 
     get; 
     set; 
    } 
} 

Luego puede escribir su función genérica como;

public void Test<T>(string a, T arg) where T : ICommonInterface 
{ 
    //do something 
} 
+0

Buena idea, excepto que no creo que pueda hacer esto porque quiero usar una cadena que es una clase sellada según un comentario anterior ... – Mansfield

+0

esto es un problema de diseño, de hecho. una función genérica se usa para realizar operaciones similares con la reutilización de código. tanto si planea realizar diferentes operaciones en el cuerpo del método, la separación de métodos es una forma mejor (en mi humilde opinión). – daryal

+0

Lo que estoy haciendo, de hecho, es escribir una función simple de registro de errores. Me gustaría que el último parámetro sea una cadena de información sobre el error, o una excepción, en cuyo caso guardo e.message + e.stacktrace como una cadena de todos modos. – Mansfield

18

Botz respuesta es 100% correcto, he aquí una breve explicación:

Cuando está escribiendo un método (genérico o no) y la declaración de los tipos de los parámetros que el método toma que está definiendo un contrato :

Si me das un objeto que sabe hacer el conjunto de cosas que tipo T sabe cómo hacer que puedo ofrecer ya sea 'a': un valor de retorno del tipo declaro, o " b ': algún tipo de comportamiento que usa ese tipo.

Si tratas de darle más de un tipo a la vez (por tener una o) o tratar de conseguir que se devuelve un valor que podría ser más de un tipo que se contraen pone borrosa:

Si me das un objeto que sepa saltar la cuerda o sepa cómo calcular pi hasta el 15º dígito, devolveré un objeto que pueda ir a pescar o quizás mezcle de hormigón.

El problema es que cuando usted entra en el método no tiene idea de si le han dado un IJumpRope o una PiFactory. Además, cuando continúe y use el método (suponiendo que lo haya compilado mágicamente), no está seguro si tiene un Fisher o un AbstractConcreteMixer. Básicamente hace que todo sea más confuso.

La solución a su problema es una de dos posibilidades:

  1. definir más de un método que define cada transformación es posible, comportamiento, o lo que sea. Esa es la respuesta de Botz. En el mundo de la programación esto se conoce como Sobrecarga del método.

  2. definir una clase base o interfaz que sabe hacer todas las cosas que necesitas para el método y tener un método toma solo ese tipo. Esto puede implicar completar un string y Exception en una clase pequeña para definir cómo planea asignarlos a la implementación, pero luego todo es súper claro y fácil de leer. Podría venir, dentro de cuatro años, leer su código y entender fácilmente lo que está pasando.

Lo que elija depende de cuán complicadas serían las opciones 1 y 2 y cuán extensible debe ser.

Así que para su situación específica voy a imaginar que estás sacando un mensaje o algo de la excepción:

public interface IHasMessage 
{ 
    string GetMessage(); 
} 

public void test(string a, IHasMessage arg) 
{ 
    //Use message 
} 

Ahora todo lo que necesita son métodos que transforman un string y un Exception a un IHasMessage. Muy fácil.

+0

sorry @ Botz3000, me acabo de dar cuenta de que escribí mal su nombre. – Crisfole

+0

O el compilador podría amenazar el tipo como un tipo de unión dentro de la función y hacer que el valor de retorno sea del mismo tipo en el que entra. TypeScript hace esto. – Alex

+0

@Alex, pero eso no es lo que hace C#. – Crisfole

Cuestiones relacionadas