Home > Software engineering >  Using IEnumerable<> as a parameter for a generic method that can get reference and non referen
Using IEnumerable<> as a parameter for a generic method that can get reference and non referen

Time:11-22

I'm trying to build a validation attribute that will validate the size of a collection, I want the method to be able to handle reference types like string and also non reference types like int. I tried to use IEnumerable<object> but that stays null in case I pass a IEnumerable<int>.

This is my current code -

[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property)]
public class EnumerableSizeAttribute : ValidationAttribute<IEnumerable<object>>
{
 
    protected override ValidationResult? IsValid(IEnumerable<object>? enumerable, ValidationContext validationContext)
    {
    //check collection size
    }
}

What should I use as parameter to allow me to pass any collection type into the method? Thanks for the help!

CodePudding user response:

Generic variance rules between value and reference types are... complex.

If you need to avoid boxing, then consider making the method itself generic and take IEnumerable<T>, so that the caller specifies it (usually implicitly, so no code change).

If that isn't an option: consider using the non-generic IEnumerable APIs. Or IList or ICollection since you're after the count, which you can then access cheaply via .Count without paying any of the boxing / iteration overheads associated with IEnumerable.

CodePudding user response:

In C# 11/.NET 7 and later you can have generic attributes :

[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property)]
public class EnumerableSizeAttribute<T> : ValidationAttribute<IEnumerable<T>>
{
 
    protected override ValidationResult? IsValid(IEnumerable<T>? enumerable, ValidationContext validationContext)
    {
    //check collection size
    }
}

Inside IsValid you can check whether enumerable is an ICollection<T>, thus using the cheap Count property to check the size :

if(enumerable is ICollection<T> col && col.Count <...)
{
    
}

You may be able to use property pattern matching too, although I'm still not comfortable with the syntax. Something like

if(enumerable is ICollection<T> {Count: < MaxLength} col)
{
    
}
  • Related