Last active
July 16, 2021 20:25
-
-
Save lydonchandra/f6e089f619cfd6b4149fc7779ace5082 to your computer and use it in GitHub Desktop.
linqpad - specs filter
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
void Main() | |
{ | |
//nameof(UserQuery.Brand).Dump(); | |
//Enum.GetName(UserQuery.Brand.Adidas).Dump(); | |
List<Product> products = new() | |
{ | |
new Product { Color = Color.Green, Size = Size.Small, Brand = Brand.Adidas }, | |
new Product { Color = Color.Red, Size = Size.Large, Brand = Brand.Adidas }, | |
new Product { Color = Color.Red, Size = Size.Medium, Brand = Brand.Nike }, | |
}; | |
ColorSpecification colorSpec = new(Color.Red); | |
SizeSpecification sizeSpec = new(Size.Medium); | |
BrandSpecification brandSpec = new(Brand.Nike); | |
//var specs2 = colorSpec & sizeSpec & brandSpec; | |
//IFilter<Product>.Filter(products, specs2).Dump(); | |
// | |
var spec1 = new EnumSpecification<Product, Color>(Color.Red); | |
var spec2 = new EnumSpecification<Product, Brand>(Brand.Nike); | |
IFilter<Product>.Filter(products, spec1 & spec2).Dump(); | |
} | |
public enum Color { | |
Red, Green, Blue | |
} | |
public enum Size { | |
Small, Medium, Large | |
} | |
public enum Brand { | |
Adidas, | |
Nike | |
} | |
public class EnumSpecification<Product, TEnum> | |
: Specification<Product> | |
where TEnum : System.Enum | |
{ | |
private TEnum enumSpec; | |
public EnumSpecification(TEnum enumSpec) => this.enumSpec = enumSpec; | |
public override bool IsSatisfied(Product item) | |
{ | |
// reflect on Product object and get Color/Size/Brand enum field | |
var propName = typeof(TEnum).Name; | |
var prop = item.GetType().GetProperty(propName) | |
?? throw new ArgumentNullException($"{propName} does not exist in Product"); | |
var propVal = prop.GetValue(item, null); | |
TEnum propValEnum = (TEnum) Enum.Parse(typeof(TEnum), propVal.ToString()); // <--------------- better way to parse enum ? | |
if(EqualityComparer<TEnum>.Default.Equals(propValEnum, this.enumSpec)) { | |
return true; | |
} | |
return false; | |
} | |
} | |
public class Product { | |
public Guid Id { get; } = Guid.NewGuid(); | |
public Color Color { get; set;} | |
public Size Size { get; set;} | |
public Brand Brand { get; set; } | |
} | |
public abstract class Specification<T> { | |
public abstract bool IsSatisfied(T item); | |
public static Specification<T> operator &(Specification<T> first, Specification<T> second) | |
{ | |
return new AndSpecification<T>( | |
new Specification<T>[] {first, second} ); | |
} | |
} | |
public class AndSpecification<T> : Specification<T> | |
{ | |
private Specification<T>[] specs; | |
public AndSpecification(params Specification<T>[] specs) | |
=> this.specs = specs; | |
public override bool IsSatisfied(T item) | |
=> this.specs.All(spec => spec.IsSatisfied(item)); | |
} | |
public interface IFilter<T> { | |
public static IEnumerable<T> Filter(IEnumerable<T> items, Specification<T> spec) { | |
foreach(var item in items) { | |
if(spec.IsSatisfied(item)) { | |
yield return item; | |
} | |
} | |
} | |
} | |
public class ColorSpecification : Specification<Product> | |
{ | |
private Color color; | |
public ColorSpecification(Color color) => this.color = color; | |
public override bool IsSatisfied(Product item) => (item.Color == this.color); | |
} | |
public class SizeSpecification: Specification<Product> | |
{ | |
private Size size; | |
public SizeSpecification(Size size) => this.size = size; | |
public override bool IsSatisfied(Product item) => (this.size == item.Size); | |
} | |
public class BrandSpecification: Specification<Product> | |
{ | |
private Brand brand; | |
public BrandSpecification(Brand brand) => this.brand = brand; | |
public override bool IsSatisfied(Product item) => (this.brand == item.Brand); | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment