views:

144

answers:

2

I am learning to use polymorphism in C#, but cannot figure out this one. I'm trying to write a class that allows me to get a filtered list of files from a repository.

The repository could be a file system folder or the embedded resources in an arbitrary already-loaded assembly (but it is not the one currently executing).

Any suggestions?

A: 
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;

namespace Reposes
{
    class ReposeFile
    {
     string m_name;

     public string Name
     {
      get { return m_name; }
     }

     public ReposeFile(string name)
     {
      m_name = name;
     }
    }

    interface IRepose
    {
     void RetriveFiles();
     ReposeFile[] Files { get; }
    }

    class FileSystemRepose : IRepose
    {
     string m_path = null;
     List<ReposeFile> m_files = new List<ReposeFile>();

     public FileSystemRepose(string path)
     {
      m_path = path;
     }

     #region IRepose Members

     public void RetriveFiles()
     {
      string[] files = Directory.GetFiles(m_path);
      foreach (string file in files)
      {
       m_files.Add(new ReposeFile(file));
      }
     }

     public ReposeFile[] Files
     {
      get { return m_files.ToArray(); }
     }

     #endregion
    }

    class AssemblyRepose : IRepose
    {
     string m_assembly = null;
     List<ReposeFile> m_files = new List<ReposeFile>();

     public AssemblyRepose(string assembly)
     {
      m_assembly = assembly;
     }

     #region IRepose Members

     public void RetriveFiles()
     {
      m_files.Add(new ReposeFile("Stuff"));
     }

     public ReposeFile[] Files
     {
      get { return m_files.ToArray(); }
     }

     #endregion
    }

    class Consumer
    {
     static void Main()
     {
      List<IRepose> reps = new List<IRepose>();
      reps.Add(new FileSystemRepose("c:\\")); // would normally be @"c:\" but stackoverflow's syntax highlighter barfed :)
      reps.Add(new AssemblyRepose("rep.dll"));

      foreach (IRepose rep in reps)
      {
       rep.RetriveFiles();

       foreach (ReposeFile file in rep.Files)
       {
        Console.WriteLine(file.Name);
       }
      }

      Console.ReadKey();
     }
    }
}

This is a crude example, but should point you in the right direction :)

nlaq
Nelson, I understand your code. But it doesn't address the hard part: how do I get from passing a string to having an Assembly object that I can call methods on? And how do I avoid loading that Assembly on every call, if it is already loaded?
David White
+3  A: 

You could define pair of interfaces like this:

public interface IReadableFile
{
    Stream OpenRead();
}

public interface IRepository
{
    IEnumerable<IReadableFile> Search(string pattern);
}

And have two different implementations of them:

public class FolderFile : IReadableFile
{
    readonly private string _name;

    public FolderFile(string name)
    {
        _name = name;
    }

    #region IFile Members

    public Stream OpenRead()
    {
        return File.OpenRead(_name);
    }

    #endregion
}

public class FolderRepository : IRepository
{
    readonly private string _directory;

    public FolderRepository(string directory)
    {
        _directory = directory;
    }

    #region IRepository Members

    public IEnumerable<IReadableFile> Search(string pattern)
    {
        return Array.ConvertAll(Directory.GetFiles(_directory, pattern), name => new FolderFile(name));
    }

    #endregion
}

public class AssemblyFile : IReadableFile
{
    readonly private Assembly _assembly;
    readonly private string _name;

    public AssemblyFile(Assembly assembly, string name)
    {
        _assembly = assembly;
        _name = name;
    }

    #region IReadableFile Members

    public Stream OpenRead()
    {
        return _assembly.GetManifestResourceStream(_name);
    }

    #endregion
}

public class AssemblyRepository : IRepository
{
    readonly private Assembly _assembly;

    public AssemblyRepository(Assembly assembly)
    {
        _assembly = assembly;
    }

    #region IRepository Members

    public IEnumerable<IReadableFile> Search(string pattern)
    {
        return _assembly.GetManifestResourceNames().Where(name => name.Contains(pattern)).Select(name => new AssemblyFile(_assembly, name)).ToArray();
    }

    #endregion
}

And then you can write your algorithms dependant on only these interfaces and not on their implementations.

Dmitriy Matveev