fork download
  1. using System;
  2. using static System.Console;
  3. using System.Collections.Generic;
  4.  
  5. public class Pessoa {
  6. public string Nome { get; set; } = "";
  7. public string Endereco { get; set; } = "";
  8. public int Idade { get; set; } = 0;
  9. public string Cargo { get; set; } = "";
  10. public decimal Salario { get; set; } = 0M;
  11. }
  12. public class ModelPessoa : Model<Pessoa> { //adotei a ideia que o Model seria para validar mesmo.
  13. override public bool Validate() {
  14. Required("Nome", Value.Nome);
  15. ValidaEndereco();
  16. Required("Cargo", Value.Cargo);
  17. Custom("Salário", "SalarioValido", "O Salário não atende os requisitos", ValidaSalario);
  18. Range("Idade", Value.Idade, Util.GetMaioridade(), 120);
  19. return (IsValid = Errors.Count == 0);
  20. }
  21. public int AsIdade(string texto) { //isto não é tão necessário, mas quis mostrar esta possibilidade
  22. var idade = 0;
  23. if (int.TryParse(texto, out idade)) {
  24. return idade;
  25. } else {
  26. Errors["IdadeInconsistente"] = new Invalidation("Idade", "A Idade foi digitada de forma inconsistente");
  27. return 0;
  28. }
  29. }
  30. public decimal AsSalario(string texto) { //isto funciona como uma abstração/encapsulamento da funcionalidade
  31. var salario = 0M;
  32. if (decimal.TryParse(texto, out salario)) {
  33. return salario;
  34. } else {
  35. Errors["SalarioInconsistente"] = new Invalidation("Salario", "O Salário foi digitado de forma inconsistente");
  36. return 0;
  37. }
  38. }
  39. private void ValidaEndereco() { //tem a vantagem de que pode mudar a regra facilmente tendo um método isolando a funcionalidade
  40. if (!Util.IsStandardAddress(Value.Endereco)) {
  41. AddError("EnderecoPadrao", "Endereço", "EnderecoPadrao", "O Endereço está em formato inválido.");
  42. }
  43. }
  44. private bool ValidaSalario(object[] values) { //feito para operar com o Custom, pode mudar as regras fácil aqui de forma canônica
  45. return ((!Errors.ContainsKey("IdadeInconsistente") && Value.Idade > 21 && Value.Salario > 1000M)) ||
  46. (Value.Cargo != "Gerente" && Value.Salario > 900M) ||
  47. (Value.Cargo == "Gerente" && Value.Salario > 1200M);
  48. }
  49. }
  50. public static class Util { //esta classe foi só para agrupar, em código real estes métodos estariam em outras classes
  51. public static string Read(string label) {
  52. Write(label);
  53. return ReadLine();
  54. }
  55. public static int GetMaioridade() => 18; //aqui poderia estar pegando de um banco de dados ou arquivo de configuração
  56. public static bool IsStandardAddress(string address) => address.Length > 2 && (address.Substring(0, 3) == "Rua" || address.Substring(0, 3) == "Av.");
  57. }
  58. public class Program {
  59. public static void Main(string[] args) {
  60. while (true) {
  61. var mp = new ModelPessoa(); //o modelo é usado para trabalhar com dados temporários
  62. mp.Messages["Idade"] = "A pessoa precisa ser maior de idade"; //personalizando uma mensagem
  63. mp.Value.Nome = Util.Read("Nome: "); //lê o dado e guarda no modelo
  64. mp.Value.Endereco = Util.Read("Endereço: ");
  65. mp.Value.Idade = mp.AsIdade(Util.Read("Idade: ")); //tentando converter
  66. mp.Value.Cargo = Util.Read("Cargo: ");
  67. mp.Value.Salario = mp.AsSalario(Util.Read("Salário: "));
  68. if (mp.Validate()) {
  69. Pessoa funcionario = mp.Value; //se está válido, então pode jogar em um objeto definitivo, gravar em DB, etc
  70. WriteLine("Cadastro efetuado!");
  71. break;
  72. } else {
  73. WriteLine("Erros ocorreram!");
  74. foreach(var erro in mp.Errors) { //tem várias formas para mostrar os erros
  75. WriteLine(erro.Value);
  76. }
  77. }
  78. }
  79. }
  80. }
  81. public class Invalidation { //uma classe simplificada para guardar dados completos sobre uma invalidade, parecido com exceção, sem ser
  82. public string Data { get; private set; }
  83. private string message;
  84. public string Message {
  85. get => message ?? "Dado inválido";
  86. }
  87. public Invalidation(string data, string message = null) {
  88. Data = data;
  89. this.message = message;
  90. }
  91. public override string ToString() => Message;
  92. }
  93. public abstract class Model<T> where T : new() { //mecanismo básico de validação separado do modelo em si. Tem maneiras mais simples de fazer o controle de erros
  94. public T Value { get; private set; } = new T();
  95. public bool IsValid { get; protected set; } = false;
  96. public Dictionary<string, Invalidation> Errors { get; protected set; } = new Dictionary<string, Invalidation>();
  97. public Dictionary<string, string> Messages { get; set; } = new Dictionary<string, string>();
  98. public abstract bool Validate();
  99. protected void Required(string field, string value) { //alguns exemplos de códigos genéricos de validação
  100. if (string.IsNullOrEmpty(value)) AddError(field + "Required", field, "Required", "O campo {0} é obrigatório.");
  101. }
  102. protected void Range(string field, int value, int min, int max) {
  103. if (value < min || value > max) AddError(field + "Range", field, "Range", "O campo {0} deve estar na faixa de {1} até {2}.", min, max);
  104. }
  105. protected void Custom(string field, string messageKey, string messageAlt, Func<object[], bool> condition, params object[] values) {
  106. if (!condition(values)) {
  107. if (values != null && values.Length > 0) {
  108. AddError(field + messageKey, field, messageKey, messageAlt, values?[0], values?[1], values?[2]); //está porco, mas não vou gastar tempo
  109. } else {
  110. AddError(field + messageKey, field, messageKey, messageAlt);
  111. }
  112. }
  113. }
  114. protected void AddError(string errorKey, string field, string messageKey, string defaultMessage, object value1 = null, object value2 = null, object value3 = null) {
  115. var message = "";
  116. Errors[errorKey] = new Invalidation(field, string.Format(Messages.TryGetValue(messageKey, out message) ? message : defaultMessage, field, value1, value2, value3));
  117. }
  118. }
  119.  
  120. //https://pt.stackoverflow.com/q/105670/101
Runtime error #stdin #stdout #stderr 0.02s 16520KB
stdin
Standard input is empty
stdout
Nome: Endereço: Idade: Cargo: Salário: 
stderr
Unhandled Exception:
System.NullReferenceException: Object reference not set to an instance of an object
  at Util.IsStandardAddress (System.String address) [0x00000] in <22be6fde63e540f5bf5fbe2154a1c317>:0 
  at ModelPessoa.ValidaEndereco () [0x0000b] in <22be6fde63e540f5bf5fbe2154a1c317>:0 
  at ModelPessoa.Validate () [0x00016] in <22be6fde63e540f5bf5fbe2154a1c317>:0 
  at Program.Main (System.String[] args) [0x00090] in <22be6fde63e540f5bf5fbe2154a1c317>:0 
[ERROR] FATAL UNHANDLED EXCEPTION: System.NullReferenceException: Object reference not set to an instance of an object
  at Util.IsStandardAddress (System.String address) [0x00000] in <22be6fde63e540f5bf5fbe2154a1c317>:0 
  at ModelPessoa.ValidaEndereco () [0x0000b] in <22be6fde63e540f5bf5fbe2154a1c317>:0 
  at ModelPessoa.Validate () [0x00016] in <22be6fde63e540f5bf5fbe2154a1c317>:0 
  at Program.Main (System.String[] args) [0x00090] in <22be6fde63e540f5bf5fbe2154a1c317>:0