variable usar son qué programacion métodos metodos metodo los estáticos estaticos estatico entre diferencia cuando atributo c# generics static methods data-access

usar - Genéricos en c#y accediendo a los miembros estáticos de T



static programacion (12)

¿Quieres hacer algo como esto?

Class test<T> { T method1(object Parameter1){ if( Parameter1 is T ) { T value = (T) Parameter1; //do something with value return value; } else { //Parameter1 is not a T return default(T); //or throw exception } } }

Desafortunadamente no se puede verificar el patrón TryParse, ya que es estático, lo que desafortunadamente significa que no es especialmente adecuado para los genéricos.

Mi pregunta se relaciona con c # y cómo acceder a los miembros estáticos ... Bueno, realmente no sé cómo explicarlo (lo que es malo para una pregunta, ¿no?) Solo les daré un código de muestra:

Class test<T>{ int method1(Obj Parameter1){ //in here I want to do something which I would explain as T.TryParse(Parameter1); //my problem is that it does not work ... I get an error. //just to explain: if I declare test<int> (with type Integer) //I want my sample code to call int.TryParse(). If it were String //it should have been String.TryParse() } }

Así que gracias por sus respuestas (por cierto, la pregunta es: ¿cómo resolvería este problema sin obtener un error). ¡Esta es una pregunta bastante fácil para ti!

Gracias, Niklas

Editar: ¡Gracias a todos por sus respuestas!

Aunque creo que la frase de prueba es la más elegante, sé por mi experiencia con vb que realmente puede ser un fastidio. Lo usé una vez y me tomó alrededor de 30 minutos ejecutar un programa, que más tarde solo tardó 2 minutos en computarse solo porque evité try - catch.

Es por eso que elegí la declaración swich como la mejor respuesta. Hace que el código sea más complicado, pero, por otro lado, me lo imagino relativamente rápido y relativamente fácil de leer. (Aunque todavía creo que debería haber una manera más elegante ... tal vez en el siguiente idioma aprenda: P)

Aunque si tienes alguna otra sugerencia, todavía estoy esperando (y dispuesto a participar)


Bien chicos: gracias por todo el pescado. Ahora con sus respuestas y mi investigación (especialmente el artículo sobre la limitación de tipos genéricos a primitivos ) les presentaré mi solución.

Class a<T>{ private void checkWetherTypeIsOK() { if (T is int || T is float //|| ... any other types you want to be allowed){ return true; } else { throw new exception(); } } public static a(){ ccheckWetherTypeIsOK(); } }


El problema es que TryParse no está definido en una interfaz o clase base en ninguna parte, por lo que no puede suponer que el tipo pasado a su clase tendrá esa función. A menos que puedas contraen T de alguna manera, te encontrarás con esto mucho.

Restricciones en los parámetros de tipo


Es posible que desee leer mi publicación anterior sobre la limitación de tipos genéricos a primitivos . Esto puede dar algunos consejos para limitar el tipo que se puede pasar al genérico (ya que TypeParse obviamente solo está disponible para un número determinado de primitivas ( string.TryParse obviamente es la excepción, lo cual no tiene sentido).

Una vez que tenga más control sobre el tipo, puede trabajar en intentar analizarlo. Es posible que necesite un pequeño interruptor feo (para llamar al TryParse correcto) pero creo que puede lograr la funcionalidad deseada.

Si necesita que le explique algo de lo anterior, pregunte :)


Esa no es la forma en que funciona la estática. Tienes que pensar en la estática como en una clase Global incluso si están distribuidas en un montón de tipos. Mi recomendación es convertirlo en una propiedad dentro de la instancia T que pueda acceder al método estático necesario.

También T es una instancia real de algo, y al igual que cualquier otra instancia, no puede acceder a la estática para ese tipo, a través del valor instanciado. Aquí hay un ejemplo de qué hacer:

class a { static StaticMethod1 () virtual Method1 () } class b : a { override Method1 () return StaticMethod1() } class c : a { override Method1 () return "XYZ" } class generic<T> where T : a { void DoSomething () T.Method1() }


Esto no es realmente una solución, pero en ciertos escenarios podría ser una buena alternativa: podemos pasar un delegado adicional al método genérico.

Para aclarar a qué me refiero, usemos un ejemplo. Digamos que tenemos un método de fábrica genérico, que debería crear una instancia de T, y queremos que llame a otro método, para notificación o inicialización adicional.

Considere la siguiente clase simple:

public class Example { // ... public static void PostInitCallback(Example example) { // Do something with the object... } }

Y el siguiente método estático:

public static T CreateAndInit<T>() where T : new() { var t = new T(); // Some initialization code... return t; }

Entonces, ahora mismo tendríamos que hacer:

var example = CreateAndInit<Example>(); Example.PostInitCallback(example);

Sin embargo, podríamos cambiar nuestro método para tomar un delegado adicional:

public delegate void PostInitCallback<T>(T t); public static T CreateAndInit<T>(PostInitCallback<T> callback) where T : new() { var t = new T(); // Some initialization code... callback(t); return t; }

Y ahora podemos cambiar la llamada a:

var example = CreateAndInit<Example>(Example.PostInitCallback);

Obviamente, esto solo es útil en escenarios muy específicos. Pero esta es la solución más limpia en el sentido de que obtenemos seguridad en tiempo de compilación, no hay "piratería" involucrada, y el código es absolutamente simple.


La única forma de hacer exactamente lo que estás buscando sería utilizar la reflexión para verificar si el método existe para T.

Otra opción es garantizar que el objeto que envíe sea un objeto convertible al restringir el tipo a IConvertible (todos los tipos primitivos implementan IConvertible). Esto le permitirá convertir su parámetro al tipo dado de manera muy flexible.

Class test<T> { int method1(IConvertible Parameter1){ IFormatProvider provider = System.Globalization.CultureInfo.CurrentCulture.GetFormat(typeof(T)); T temp = Parameter1.ToType(typeof(T), provider); } }

También podría hacer una variación al usar un tipo ''objeto'' en su lugar como lo hizo originalmente.

Class test<T> { int method1(object Parameter1){ if(Parameter1 is IConvertible) { IFormatProvider provider = System.Globalization.CultureInfo.CurrentCulture.GetFormat(typeof(T)); T temp = Parameter1.ToType(typeof(T), provider); } else { // Do something else } } }


Mejor código: restringe T a ValueType de esta manera:

class test1<T> where T: struct

Una "estructura" aquí significa un tipo de valor. String es una clase, no un tipo de valor. int, float, Enums son todos tipos de valores.

Por cierto, el compilador no acepta llamar a métodos estáticos o acceder a miembros estáticos en ''parámetros de tipo'' como en el siguiente ejemplo que no compilará :(

class MyStatic { public static int MyValue=0; } class Test<T> where T: MyStatic { public void TheTest() { T.MyValue++; } }

=> Error 1 ''T'' es un ''parámetro de tipo'', que no es válido en el contexto dado

SL.


Para acceder a un miembro de una clase o interfaz específica, debe usar la palabra clave Where y especificar la interfaz o clase base que tiene el método.

En la instancia anterior, TryParse no proviene de una interfaz o clase base, por lo que lo que intenta hacer arriba no es posible. Lo mejor es usar Convert.ChangeType y una instrucción try / catch.

class test<T> { T Method(object P) { try { return (T)Convert.ChangeType(P, typeof(T)); } catch(Exception e) { return null; } } }


Probablemente no puedas hacerlo.

En primer lugar, si fuera posible, necesitaría un límite más estricto en T, por lo que el registrador de tipos podría estar seguro de que todas las sustituciones posibles para T en realidad tenían un método estático llamado TryParse.


Respuesta corta, no puedes.

Respuesta larga, puedes hacer trampa:

public class Example { internal static class Support { private delegate bool GenericParser<T>(string s, out T o); private static Dictionary<Type, object> parsers = MakeStandardParsers(); private static Dictionary<Type, object> MakeStandardParsers() { Dictionary<Type, object> d = new Dictionary<Type, object>(); // You need to add an entry for every type you want to cope with. d[typeof(int)] = new GenericParser<int>(int.TryParse); d[typeof(long)] = new GenericParser<long>(long.TryParse); d[typeof(float)] = new GenericParser<float>(float.TryParse); return d; } public static bool TryParse<T>(string s, out T result) { return ((GenericParser<T>)parsers[typeof(T)])(s, out result); } } public class Test<T> { public static T method1(string s) { T value; bool success = Support.TryParse(s, out value); return value; } } public static void Main() { Console.WriteLine(Test<int>.method1("23")); Console.WriteLine(Test<float>.method1("23.4")); Console.WriteLine(Test<long>.method1("99999999999999")); Console.ReadLine(); } }

Creé un diccionario estático que contenía un delegado para el método TryParse de todos los tipos que quisiera usar. Luego escribí un método genérico para buscar el diccionario y pasar la llamada al delegado apropiado. Como cada delegado tiene un tipo diferente, solo los guardo como referencias de objetos y los redirecciono al tipo genérico apropiado cuando los recupero. Tenga en cuenta que, por el simple ejemplo, he omitido la comprobación de errores, por ejemplo, para verificar si tenemos una entrada en el diccionario para el tipo dado.


Una forma más de hacerlo, esta vez un reflejo en la mezcla:

static class Parser { public static bool TryParse<TType>( string str, out TType x ) { // Get the type on that TryParse shall be called Type objType = typeof( TType ); // Enumerate the methods of TType foreach( MethodInfo mi in objType.GetMethods() ) { if( mi.Name == "TryParse" ) { // We found a TryParse method, check for the 2-parameter-signature ParameterInfo[] pi = mi.GetParameters(); if( pi.Length == 2 ) // Find TryParse( String, TType ) { // Build a parameter list for the call object[] paramList = new object[2] { str, default( TType ) }; // Invoke the static method object ret = objType.InvokeMember( "TryParse", BindingFlags.InvokeMethod, null, null, paramList ); // Get the output value from the parameter list x = (TType)paramList[1]; return (bool)ret; } } } // Maybe we should throw an exception here, because we were unable to find the TryParse // method; this is not just a unable-to-parse error. x = default( TType ); return false; } }

El siguiente paso sería tratar de implementar

public static TRet CallStaticMethod<TRet>( object obj, string methodName, params object[] args );

Con la coincidencia de tipo de parámetro completo, etc.