Mixing Generics in C#
c#Recently I've come across a situation where I wanted to keep a list of validation errors for fields with different data types. Upon writing this article, however, I realized that I could have simplified the entire process by keeping the expected and actual values all as strings. Since I went through the trouble of going through this, though, I will share what I learned in case it might help in a different situation.
First, we have to create a validation error type. In order to be able to create a list of them, though, you will need to create an interface so that they don't all need to have the same data type.
public interface IValidationError
{
string FieldName { get; set; }
Type Type { get; }
object ExpectedValue { get; set; }
object ActualValue { get; set; }
}
public class ValidationError<T> : IValidationError
{
public string FieldName { get; set; } = string.Empty;
Type IValidationError.Type => typeof(T);
public T ExpectedValue { get; set; }
public T ActualValue { get; set; }
object IValidationError.ExpectedValue
{
get => ExpectedValue.ToString();
set => ExpectedValue = (T)value;
}
object IValidationError.ActualValue
{
get => ActualValue.ToString();
set => ActualValue = (T)value;
}
}
Now you should be able to use the new class to create a list like so:
List<IValidationError> validationErrors = new();
validationErrors.Add(new ValidationError<string>()
{
FieldName = "Reference",
ExpectedValue = "A26",
ActualValue = "A33"
});
validationErrors.Add(new ValidationError<decimal>()
{
FieldName = "Cost",
ExpectedValue = 9.99m,
ActualValue = 10.99m
});
To get the values and use them:
string errorMessageFormat = "{0} was supposed to be {1} but is instead {2}. ";
string errorMessage = "";
if (validationErrors.Count > 0)
{
foreach (var error in validationErrors)
{
errorMessage += string.Format(errorMessageFormat, error.FieldName, error.ExpectedValue, error.ActualValue);
}
Console.WriteLine(errorMessage.Trim());
}
Here's the whole example:
using System;
namespace MixingGenerics
{
internal class Program
{
static void Main()
{
List<IValidationError> validationErrors = new();
validationErrors.Add(new ValidationError<string>()
{
FieldName = "Reference",
ExpectedValue = "A26",
ActualValue = "A33"
});
validationErrors.Add(new ValidationError<decimal>()
{
FieldName = "Cost",
ExpectedValue = 9.99m,
ActualValue = 10.99m
});
string errorMessageFormat = "{0} was supposed to be {1} but is instead {2}. ";
string errorMessage = "";
if (validationErrors.Count > 0)
{
foreach (var error in validationErrors)
{
errorMessage += string.Format(errorMessageFormat, error.FieldName, error.ExpectedValue, error.ActualValue);
}
Console.WriteLine(errorMessage.Trim());
}
}
}
public interface IValidationError
{
string FieldName { get; set; }
Type Type { get; }
object ExpectedValue { get; set; }
object ActualValue { get; set; }
}
public class ValidationError<T> : IValidationError
{
public string FieldName { get; set; } = string.Empty;
Type IValidationError.Type => typeof(T);
public T ExpectedValue { get; set; }
public T ActualValue { get; set; }
object IValidationError.ExpectedValue
{
get => ExpectedValue.ToString();
set => ExpectedValue = (T)value;
}
object IValidationError.ActualValue
{
get => ActualValue.ToString();
set => ActualValue = (T)value;
}
}
}
- Next: Fast Inserts to Oracle with C#
- Previous: Simple Regex Example in C#