Dado:
List<string> args Digamos que tengo un List<string> args de la siguiente manera:
List<string> args = new List<string> { "1", "helloworld", "3" } Quiero validar args de la siguiente manera, puedo llamar a cualquiera de estos métodos según lo requiera mi código.
Métodos de validación:
public bool isValidOneString(List<string> args) { return args.Count() == 1; } public bool isValidTwoStrings(List<string> args) { return args.Count() == 2; } public bool isValidThreeStrings(List<string> args) { return args.Count() == 3; } public bool isValidOneStringTwoFloat(List<string> args) { bool isValid = args.Count() == 2; if(!isValid) return false; float valueAfterParse; isValid = float.TryParse(args[1], out valueAfterParse); return isValid; } public bool isValidOneFloatTwoDoubleThreeInt32(List<string> args) { bool isValid = args.Count() == 3; if(!isValid) return false; float valueAfterParse; isValid = float.TryParse(args[0], out valueAfterParse); if(!isValid) return false; Double valueAfterParse; isValid = Double.TryParse(args[0], out valueAfterParse); if(!isValid) return false; Int32 valueAfterParse; isValid = Int32.TryParse(args[0], out valueAfterParse); if(!isValid) return false; return isValid; }Problema: como puede ver, eventualmente terminaré teniendo una cantidad infinita de métodos de validación. ¿Hay alguna manera de que pueda tener solo 1 método de validación como por ejemplo a continuación? (que puede hacerse cargo de todos los casos posibles)
public bool isValid(List<string> args, int totalExpectedCountOfArgs, List<string> typesOfEachArg) { bool isValid = args.Count() == totalExpectedCountOfArgs; if(!isValid) return false; int i = 0; foreach(string dataType : typesOfEachArg) { isValid = typeOf(dataType).TryParse(args[i], out typeOf(dataType)); //I AM GETTING ERROR HERE BECAUSE I DONT KNOW HOW TO GENERIFY THIS if(!isValid) return false; i++; } return true; } Y luego puedo simplemente llamar al método anterior isValid(args, 3, List<string>{"float", "Int32", "Double"}) ? Pero recibo un error en mi método genérico, ¿alguien sabe cómo validar los tipos de datos de forma genérica y dinámica?
Qué tal si:
public bool IsValid<TArg0>(List<string> args) => args.Length == 1 && IsValidCore<TArg0>(args[0]); public bool IsValid<TArg0, TArg1>(List<string> args); => args.Length == 2 && IsValidCore<TArg0>(args[0]) && IsValidCore<TArg1>(args[1]); public bool IsValid<TArg0, TArg1, TArg2>(List<string> args) => args.Length == 3 && IsValidCore<TArg0>(args[0]) && IsValidCore<TArg1>(args[1]) && IsValidCore<TArg2>(args[2]); Entonces, en lugar de isValidOneFloatTwoDoubleThreeInt32(args) , usaría IsValid<float, double, int>(args);
etc, para un pequeño límite superior N de TArgN . El problema es que necesita un analizador basado en genéricos, pero eso no es necesariamente tan malo si solo necesita poder manejar tipos específicos, al marcar la T :
private static bool IsValidCore<T>(string value) { if (typeof(T) == typeof(string)) return true; if (typeof(T) == typeof(int)) return int.TryParse(...); if (typeof(T) == typeof(float)) return float.TryParse(...); if (typeof(T) == typeof(double)) return double.TryParse(...); // etc for some finite number of types throw new NotSupportedException("Not considered: " + typeof(T).Name); }Tal vez ayudaría si tuviera un método de este tipo para validadores arbitrarios:
public static bool IsValid<T>(List<T> args, params Func<List<T>, bool>[] validators) => validators.All(validate => validate(args));Ahora podría, por ejemplo, validarlo de esta manera:
bool isValid = IsValid(args, list => list.Count == 3);o con un método existente:
bool isValid = IsValid(args, isValidThreeStrings);o con ambos:
bool isValid = IsValid(args, list => list.Count == 3, isValidThreeStrings);o con múltiples:
Func<List<string>, bool>[] allValidators = new Func<List<string>, bool>[] { list => list.Count == 2, list => float.TryParse(list[1], out float val), isValidOneFloatTwoDoubleThreeInt32 }; bool isValid = IsValid(args, allValidators); Si te gusta y quieres reutilizarlo para todo tipo de listas/matrices/lo que sea, puedes crear un método de extensión como este. Tenga en cuenta que se necesita IEnumerable<T> :
public static class EnumerableExtensions { public static bool AreAllValid<T>(this IEnumerable<T> items, params Func<IEnumerable<T>, bool>[] validators) => validators?.All(validate => validate(items)) ?? throw new ArgumentNullException(nameof(validators)); }Sugiero extraer model , que sea un diccionario con Type como clave y validator Func como valor:
private static Dictionary<Type, Func<string, bool>> s_Validators = new Dictionary<Type, Func<string, bool>>() { {typeof(string), (x) => true }, {typeof(int), (x) => int.TryParse(x, out var _) }, {typeof(float), (x) => float.TryParse(x, out var _) }, {typeof(double), (x) => double.TryParse(x, out var _) }, //TODO: add more type validators here };Entonces el validador puede ser:
private static bool IsValid(IEnumerable<string> arguments, params Type[] signature) { if (null == arguments) return false; // or throw ArgumentNullException if (null == signature) return false; int index = 0; foreach (string arg in arguments) { // Too many arguments if (index >= signature.Length) return false; // For argument to be valid we should know type and pass validation if (!s_Validators.TryGetValue(signature[index++], out var validator) || !validator(arg)) return false; } // if index < signature.Length we have too few arguments return index == signature.Length; }Uso:
List<string> args = new List<string> { "1", "helloworld", "3" }; bool isValid = IsValid(args, typeof(int), typeof(string), typeof(int)); Si desea tener string s en lugar de Type s, cambie s_Validators e IsValid un poco:
private static Dictionary<string, Func<string, bool>> s_Validators = new Dictionary<string, Func<string, bool>>() { {"string", (x) => true }, {"int", (x) => int.TryParse(x, out var _) }, ... }; private static bool IsValid(IEnumerable<string> arguments, params string[] signature) { ... }