3

How can I pass an extension method as an argument to the constructor of a class, and have methods in that class use that extension as an extension?

For example: this is a file which contains an IEnumerable extension method:

namespace System.Collections.Generic
{
    public static partial class IEnumerableMethodExtensions
    {
        /// <summary>
        /// Used by IsImageFile to determine if a file is a graphic type
        /// we care about. (Not an Extension Method, just a helper method)
        /// </summary>
        public static string[] GraphicFileExtensions = new string[] { ".png", ".bmp", ".gif", ".jpg", ".jpeg" };

        /// <summary>
        /// Method Extension - specifies that FileInfo IEnumerable should only 
        /// return files whose extension matches one in GraphicFileExtensions[]. 
        /// </summary>
        /// <param name="files"></param>
        /// <returns></returns>
        public static IEnumerable<FileInfo> IsImageFile(this IEnumerable<FileInfo> files)
        {
            foreach (FileInfo file in files)
            {
            string ext = file.Extension.ToLower();
            if (GraphicFileExtensions.Contains(ext))
                yield return file;
        }
    }
 }

}

I want to be able to pass IsImageFile() as an argument to a constructor to this object, so that methods in that class can use the IsImageFile as a method extension:

public class MainFileInfoSource
{
    public MainFileInfoSource(List<DirectoryInfo> Directories,
                 ENUMERABLE_METHOD_EXTENSION_FILE_FILTER TheMethodExtension)
    {
        _myFilterMethodExtension = TheMethodExtension;
        _directories = Directories;

        initializeFileInfoList();
    }

    ...

    /// <summary>
    /// Initializes the Files list.
    /// </summary>
    private void initializeFileInfoList()
    {
     ...
        for (int i = 0; i < _directories.Count; i++)
        {
            iEnumerableFileInfo = new[] { _directories[i] }.Traverse(dir =>
            getDirectoryInfosWithoutThrowing(dir)).SelectMany(dir =>
            getFileInfosWithoutThrowing(dir)._myFilterMethodExtension());
Cardinal Fang
  • 283
  • 2
  • 12
  • 1
    http://stackoverflow.com/questions/1016033/extension-methods-defined-on-value-types-cannot-be-used-to-create-delegates-wh – AlexanderBrevig Sep 19 '14 at 22:58
  • @AlexanderBrevig can you help me understand what portion of that question/answer is my take-away? Apparently I am not smart enough to see the connection. – Cardinal Fang Sep 19 '14 at 23:00
  • You can't do that because static method calls are compiled at compile time. Explaining what your actual goal is may help with finding good looking approach (like extension method implemented by using service locator pattern to find implementation). – Alexei Levenkov Sep 19 '14 at 23:15

2 Answers2

3

How can I pass an extension method as an argument to the constructor of a class... ?

Make the argument a delegate that matches the signature you're looking for. For example, Func<IEnumerable<FileInfo>, IEnumerable<FileInfo>> would work in the case you gave.

public MainFileInfoSource(List<DirectoryInfo> Directories,
             Func<IEnumerable<FileInfo>, IEnumerable<FileInfo>> FilterMethod)

Note that in this case you will either have to identify the method using its static location, or using a lambda expression:

var source = new MainFileInfoSource(directories, FileFilterExtensions.IsAwesome);
var source2 = new MainFileInfoSource(directories, f => f.IsAwesome());

... , and have methods in that class use that extension as an extension?

This cannot be done. Extension methods are just special syntax sugar, and have a lot of rules around how they can be used. This information does not transfer when you create a delegate from the extension method. So you have to call the delegate as a regular method.

        iEnumerableFileInfo = new[] { _directories[i] }
            .Traverse(dir => getDirectoryInfosWithoutThrowing(dir))
            .SelectMany(dir => _myFilterMethod(getFileInfosWithoutThrowing(dir)));
StriplingWarrior
  • 151,543
  • 27
  • 246
  • 315
  • Thanks. This should work. I really wanted to express it as an IEnumerable method extension, and that was blinding me to any other approach. Too bad though, I love stacking up method extensions. :-) – Cardinal Fang Sep 19 '14 at 23:28
  • I suppose I could also write a "generic" IEnumerable filter extension method that takes the filter string as an argument, and pass that filter string into my constructor, also. If I truly want to force method extensions. – Cardinal Fang Sep 19 '14 at 23:30
0

Does not it work for you?

public class MainFileInfoSource
{
    public MainFileInfoSource(List<DirectoryInfo> Directories,
        Func<IEnumerable<FileInfo>, IEnumerable<FileInfo>> TheMethodExtension)
    {

    }
}

and

  new MainFileInfoSource(null, IEnumerableMethodExtensions.IsImageFile);

?

Valentin P.
  • 1,131
  • 9
  • 18
  • Valentin, I literally could not figure out how to get the method extension to show up after the dot in getFileInfosWithoutThrowing() (via Intellisense). I think Alexander and StriplingWarrior have clarified for me that C# doesn't want me to try and express the delegate as a method extension. – Cardinal Fang Sep 19 '14 at 23:24