Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Calling generic methods

Robert Johnson edited this pageOct 2, 2022 ·4 revisions

We're talking here about generic methods, not methods in generic classes.

The use of generic methods in .Net libraries has increased significantly in recent years. Linq, for example, relies on generic methods as extension methods to other types. Linq makes significant use of dynamic call sites and type inferencing on generic method type parameters for its magic.

ClojureCLR shares some of that magic.

(import 'System.Linq.Enumerable)(def r1 (Enumerable/Where [1 2 3 4 5] even?))(seq r1)                             ; => (2 4)

The generic methodWhere is overloaded with the following signatures.

public static IEnumerable<TSource> Where<TSource>(        this IEnumerable<TSource> source,        Func<TSource, bool> predicate)public static IEnumerable<TSource> Where<TSource>(        this IEnumerable<TSource> source,        Func<TSource, int, bool> predicate)

When analyzing(Enumerable/Where [1 2 3 4 5] even?), the[1 2 3 4 5] is aclojure.lang.PersistentVector, which supportsIEnumerable<Object>and hence can match against theIEnumerable<TSource> in the first argument position. The value ofeven? is aclojure.lang.IFn, specifically aclojure.lang.AFn. In ClojureCLR,clojure.lang.AFn implements interfaces related to reporting the number of arguments supported in calls and to participating in generic type parameter inferencing. The methodeven? reports that it supports one argument and does not support two arguments. Therefore, it supports casting toFunc<TSource, bool> but not toFunc<TSource, int, bool>, allowing discrimination between the two overloads. (Func<TSource, bool> is a delegate for a method taking one argument of typeTSource and returning a value of type 'bool'.)

If we had a functionf that supports one and two arguments, the type inferencing illustrated in the previous example would not work.

user=> (Enumerable/Where [1 2 3 4 5] f)ArgumentTypeException Multiple targets could match:    Where(IEnumerable`1, Func`2),    Where(IEnumerable`1, Func`3)  .CallSite.Target (:0)

There are two ways around this. One way is to write a simple anonymous function taking one argument that calls f:

(Enumerable/Where [1 2 3 4 5] #(f %1))

The second way is to explicitly declare types. Macrossys-func andsys-action are available to create from anIFn delegates of typeSystem.Func<,...> andSystem.Action<,...>:

(Enumerable/Where [1 2 3 4 5] (sys-func [Object Boolean] [x] (f x))))

The first is preferable. However, when type inferencing does not sufficesys-func andsys-action can allow you to do the inference for the system.

(def r2 (Enumerable/Range 1 10))(seq r2)                             ;=> (1 2 3 4 5 6 7 8 9 10)(Enumerable/Where r2 even?)          ;=> (2 4 6 8 10)

This used to fail due to problems with Range iterator conversions, but it works now. However, I'll still use it as an example of how to usesys-func:

(Enumerable/Where r2 (sys-func [Int32 Boolean] [x] (even? x)))

BTW, the following might work if r2 did not support IEnumerable in some flavor but did support conversion to anISeq:

(Enumerable/Where (seq r2) (even? x))

This works because the type implementing the sequence onr2does supportIEnumerable<Object>.

There are some cases where you need to explicitly supply type arguments to a generic method. For example, the following fails:

(def r3 (Enumerable/Repeat 2 5)   ;=> FAILS

The error message states:

InvalidOperationException Late bound operations cannot be performed on types ormethods for which ContainsGenericParameters is true. System.Reflection.RuntimeMethodInfo.ThrowNoInvokeException (:0)

We can cause the type arguments on theRepeat<T> method to be filled in using thetype-args macro:

(def r3 (Enumerable/Repeat (type-args Int32) 2 5))                ; use type-args to supply the type parameters to the generic method(seq r3)        ;=> (2 2 2 2 2)
Clone this wiki locally

[8]ページ先頭

©2009-2025 Movatter.jp