Note that there are some explanatory texts on larger screens.

plurals
  1. POHow could an extension method be attached to a generic class when the type argument is IEnumerable<T>?
    primarykey
    data
    text
    <p>As a hobby project (and to immerse myself more deeply in generics/extension methods), I'm writing a parameter checking library!</p> <p>I have a model called Argument that describes a parameter and looks like this:</p> <pre><code>public class Argument&lt;T&gt; { internal Argument(string name, T value) { Name = name; Value = value; } public string Name { get; private set; } public T Value { get; private set; } } </code></pre> <p>When validation for a parameter begins, an instance of this object is created, and individual validations are performed by invoking extension methods (that contain the actual logic) that hang off of it.</p> <p>One such extension method verifies that a collection contains at least one item, and currently looks like this:</p> <pre><code>public static Argument&lt;IEnumerable&lt;T&gt;&gt; HasItems&lt;T&gt;(this Argument&lt;IEnumerable&lt;T&gt;&gt; argument) { if (!argument.Value.Any()) throw Error.Generic(argument.Name, "Collection contains no items."); return argument; } </code></pre> <p>But it doesn't appear to work. If I were, say, to write this unit test:</p> <pre><code>[TestMethod] public void TestMethod1() { var argument = new List&lt;int&gt;() { 1, 2, 6, 3, -1, 5, 0 }; Validate.Argument("argument", argument) .IsNotNull() .HasItems() .All(v =&gt; v.IsGreaterThan(0)); } </code></pre> <p>HasItems doesn't show up in Intellisense, <em>and</em> I get this compile error:</p> <blockquote> <p><code>'Validation.Argument&lt;System.Collections.Generic.List&lt;int&gt;&gt;'</code> does not contain a definition for 'HasItems' and no extension method 'HasItems' accepting a first argument of type <code>'Validation.Argument&lt;System.Collections.Generic.List&lt;int&gt;&gt;'</code> could be found (are you missing a using directive or an assembly reference?)</p> </blockquote> <p>And if I try passing the value directly into the extension method, like so:</p> <pre><code>CollectionTypeExtensions.HasItems(Validate.Argument("argument", argument)); </code></pre> <p>I get this:</p> <blockquote> <p>The best overloaded method match for <code>'Validation.CollectionTypeExtensions.HasItems&lt;int&gt;(Validation.Argument&lt;System.Collections.Generic.IEnumerable&lt;int&gt;&gt;)'</code> has some invalid arguments</p> </blockquote> <p>Based on my research, what I'd need for this to work is called "variance," and applies to interfaces and delegates, but not to classes (ie: all classes are invariant.)</p> <p>That said, it might work another way. One that comes to mind is to rewrite it to go straight to T, like so:</p> <pre><code>public static Argument&lt;T&gt; HasItems&lt;T, TElement&gt;(this Argument&lt;T&gt; argument) where T : IEnumerable&lt;TElement&gt; { if (!argument.Value.Any()) throw Error.Generic(argument.Name, "Collection contains no items."); return argument; } </code></pre> <p>..But that doesn't work either because it requires TElement to be specified explicitly when the method is invoked. I could also fall back to using the non-generic IEnumerable interface in the type constraint, but then I'd have to either find a way to coerce the IEnumerable into IEnumerable (which would require knowing what T is in that context), or duplicating the functionality of Any() in order to test for the existence of any items, <em>and</em> there's another extension method (All) that that would be very, very messy for, so I'd rather avoid it.</p> <p>So ultimately, I guess my question is: how do I get my extension method to attach properly?</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload