C#:convierte una lista de cadenas en un conjunto de enumeraciones

C#:convierte una lista de cadenas en un conjunto de enumeraciones

Supongamos que tiene una lista de códigos de estado HTTP que lee cuando se inicia el servicio (quizás desde appsettings.json o desde la base de datos). Cada vez que envía una solicitud HTTP, desea verificar si el código de estado devuelto está en esta lista de códigos de estado. Para hacer las cosas más eficientes, desea convertir la lista de códigos de estado en un HashSet.

Para convertir una cadena en una enumeración, puede usar Enum.Parse(). Para convertir una lista completa en un conjunto de enumeraciones, puede hacer lo siguiente:

new HashSet<HttpStatusCode>(statusCodes.Select(s => Enum.Parse<HttpStatusCode>(s)));
Code language: C# (cs)

En el resto de este artículo, mostraré el código/las pruebas de un convertidor genérico que filtra los valores no válidos.

Lista genérica a HashSet método de extensión del convertidor

Quiero crear un método de extensión de convertidor genérico que tenga el siguiente comportamiento:

  • Convierte una lista de cadenas (valores de enumeración, no nombres) en un conjunto de enumeraciones.
  • Filtra cadenas nulas y espacios en blanco.
  • Filtra los valores de enumeración no válidos.

Puede usar esto como punto de partida y personalizar el comportamiento como desee. Por ejemplo, es posible que desee lanzar una excepción si se detecta algún valor no válido, en lugar de simplemente filtrarlo.

Pruebas

Para construir este convertidor, primero agregué las siguientes pruebas unitarias una a la vez:

[TestClass()]
public class ListExtensionsTests
{
	[TestMethod()]
	public void TestToSet_HappyPath()
	{
		//arrange
		var list = new List<string>() { "408", "411", "412", "413", "415" };
		var expectedSet = new HashSet<HttpStatusCode>()
		{
			(HttpStatusCode)408,
			(HttpStatusCode)411,
			(HttpStatusCode)412,
			(HttpStatusCode)413,
			(HttpStatusCode)415
		};

		//act
		var set = list.ToSet<HttpStatusCode>();

		//assert
		CollectionAssert.AreEquivalent(expectedSet.ToList(), set.ToList());
	}
	[TestMethod()]
	public void TestToSet_FiltersOutNullAndWhitespaceStrings()
	{
		//arrange
		var list = new List<string>() { "408", null, "", " " };
		var expectedSet = new HashSet<HttpStatusCode>()
		{
			(HttpStatusCode)408
		};

		//act
		var set = list.ToSet<HttpStatusCode>();

		//assert
		CollectionAssert.AreEquivalent(expectedSet.ToList(), set.ToList());
	}
	[TestMethod()]
	public void TestToSet_FiltersOutInvalidEnumValues()
	{
		//arrange
		var list = new List<string>() { "999", "abc" };
		var expectedSet = new HashSet<HttpStatusCode>()
		{
		};

		//act
		var set = list.ToSet<HttpStatusCode>();

		//assert
		CollectionAssert.AreEquivalent(expectedSet.ToList(), set.ToList());
	}
}
Code language: C# (cs)

Código

El siguiente método de extensión implementa el comportamiento de conversión de lista a conjunto:

using System.Collections.Generic;
using System.Linq;

public static class ListExtensions
{
	public static HashSet<T> ToSet<T>(this List<string> statusCodes) where T : Enum
	{
		return new HashSet<T>(statusCodes.Where(s => !string.IsNullOrWhiteSpace(s)
			&& Int32.TryParse(s, out int intValue)
			&& Enum.IsDefined(typeof(T), intValue))
		.Select(s => (T)Enum.Parse(typeof(T), s)));
	}
}
Code language: C# (cs)

Estoy usando Int32.TryParse() + Enum.IsDefined() en lugar de solo Enum.Parse(), porque solo quiero aceptar valores de enumeración (no nombres) y solo quiero agregar valores de enumeración válidos al conjunto. El problema con Enum.Parse() es que devuelve un objeto de enumeración incluso si no hay un valor de enumeración coincidente (por ejemplo:Enum.Parse(“999”) devuelve un objeto HttpStatusCode, aunque no hay un código de estado con el valor de 999).

Aquí hay un ejemplo del uso de este método de extensión:

//Get the list of strings from somewhere, like appsettings.json or the database
var list = new List<string>() { "408", "411", "412", "413", "415" };

//Convert to a set for efficient lookups later on
var statusCodeSet = list.ToSet<HttpStatusCode>();


Console.WriteLine(string.Join(Environment.NewLine, statusCodeSet));
Code language: C# (cs)

Esto genera lo siguiente:

RequestTimeout
LengthRequired
PreconditionFailed
RequestEntityTooLarge
UnsupportedMediaTypeCode language: plaintext (plaintext)