再帰

2011-07-05 19 views
1

私はScanItemのリストとツリービューを構築していてHierarchicalDataて検索します。 ScanItemのクラスは、実際にある:私は私のツリービュー内のフォルダやファイルを区別するために、2つの他のクラスを作成するために必要なツリービューを作成するために再帰

public class ScanItem 
    { 
     public string FullPath { get; set; } 
     public string Name 
     { 
      get 
      { 
       return Path.GetFileName(FullPath); 
      } 

     } 
     public DateTime ModifiedDate { get; set; } 
     public DateTime CreatedDate { get; set; } 
     public FileAttributes Attributes { get; set; } 
     public bool IsDirectory { get; set; } 


     public string Extension 
     { 
      get 
      { 
       if (IsDirectory) 
        return "Folder"; 
       else 
        return Path.GetExtension(Name); 
      } 
     } 

     public UInt64 Size { get; set; }    
    } 

public class ScanFile : ScanItem 
    { 

    } 
    public class ScanDir : ScanItem 
    { 
     public List<ScanItem> Items { get; set; } 
     public ScanDir() 
     { 
      Items = new List<ScanItem>(); 
     }     
    } 

に留意されたいです。クラスScanFileはちょうどScanItemのようなもので、SCANDIRクラスは、アイテムと呼ばれる余分な性質を持っており、自身の項目のリストが含まれます。

だから私、このdirecotoryを反復処理する(C:\ Tempに):もしenter image description here

私のリストが実際に含まれています:

enter image description here

ノートを私は1つのSCANDIRオブジェクトを展開した場合、私がすること目を移入するために

enter image description here

:別のリストを取得しますe。次のツリービュー:

enter image description here

だから私は、特定のパスにあるファイルやディレクトリを検索することによって、再帰を使用して、このリストを移入することができました。

がツリービューをフィルタリングすることができますインターネットでのいくつかの場所があり、それは私が実際に何をしたいですので、私はちょうど私の状況を説明したかったです。

が、私は実際に私の結果をフィルタリングするために、次の再帰的な方法を使用して試してみました:私は、リスト内の各項目を反復処理し、その後、いくつかの基準が満たされない場合、それを削除することができればしかし、それは素晴らしいことでしょう。

public List<ScanItem> search(List<ScanItem> items) 
    { 
     var filter = new List<ScanItem>(); 

     foreach (var item in items) 
     { 
      if (!item.FullPath.Contains("stringIwantToLookFor")) continue; 
      filter.Add(item); 
      if (item.IsDirectory) 
      { 
       search(((ScanDir)item).Items);      
      }     
     } 

     return filter; 
    } 

私はアイテムが発見された場合、私はすべての親のルートディレクトリを追加する必要があると思いますし、それが動作しない理由です。私はspesific基準に基づいて、ツリービューをフィルタリングすることができるようにしたいので、私は私自身の再帰方法を構築したい理由があります。

EDIT:つまり

私は私のリストビューで「X.TXT」を含むすべてのアイテムを持っているしたい場合、私はちょうど見たい: enter image description here

+0

"IsDirectory"チェックはコード "匂い"です。処理しているオブジェクトのタイプに基づいて条件付きロジックを使用して動作を定義することは多態性ではなく、頻繁に積極的に使用すると読みにくく、保守が難しいコードになります。検索に対処するには、「訪問者」のデザインパターンを調べる方がよいでしょう。 –

答えて

2

私はこれを次のようにします:ScanItempublic abstract ScanItem Seach(string s)を作成します。検索したい文字列で呼び出すことができます。

実際の実装は次のようになります。

ScanFile

public override ScanItem Seach(string s) 
{ 
    if (Name.Contains(s)) 
     return this; 

    return null; 
} 

ScanDirScanFile

public override ScanItem Seach(string s) 
{ 
    var results = Items.Select(i => i.Seach(s)).Where(i => i != null).ToList(); 
    if (results.Any()) 
    { 
     var result = (ScanDir)MemberwiseClone(); 
     result.Items = results; 
     return result; 
    } 

    return null; 
} 

実装は簡単です:ファイルが一致する場合には、他に、それを返します返信nullScanDirでは、すべての子アイテムに対して再帰的にSearchを呼び出します。それらのいずれかがnullを返していない場合は、現在のオブジェクトのコピーを作成し、コピーのItemsを一致したもののみに設定します。一致しない場合は、nullを返します。

これは、ディレクトリではなくファイルの名前だけを検索することに注意してください。しかし、あなたがそれをしたいならば、そのような変更は簡単になるでしょう。

+0

ワウは印象的な多くのおかげであった。それはまさに私が探していたものでした。 –

0

私はあなたのポストに自分のコメントを実現十分に記述されていない可能性がありますので、私は私がなっていたものを示すために、いくつかのC#-ish擬似コードを書きました。

ここで多型、疎結合な方法で検索を実装するためにVisitorパターンを使用しての例です:

interface FilesystemVistor 
{ 
    void Visit (FilesystemItem item); 
} 
interface FilesystemItem 
{ 
    void Accept(FilesystemVistor visitor); 
    string Name; 
} 

class Directory : FilesystemItem 
{ 
    private FilesystemItem[] _children; 
    public void Accept(FilesystemVistor visitor) { 
    visitor.Visit(this); 
    foreach(FilesystemItem item in _children) 
    { 
     visitor.Visit(item); 
    } 
    } 
} 
class File : FilesystemItem 
{ 
    public void Accept(FilesystemVistor visitor) { 
    visitor.Visit(this); 
    } 
} 

class FilesystemSearcher : FilesystemVistor 
{ 
    private List<string> _results; 
    public void Visit(FilesystemItem item) { 
    if (item.Name == "Foo") { _results.Add(item.Name); } 
    } 
} 

この「ビジターパターンは」デザインはあなたがなくても、検索のいずれかの種類を実装することができますベースファイルシステムとファイルシステムの構造について何かを "知る"検索アルゴリズムでは、実装の詳細を公開するために "IsDirectory"のような追加のプロパティは必要ありません。

+0

私はFilesystemSearcherのリストを作成しますか?私はこれを私のツリービューにどのように結びつけるのですか?申し訳ありませんが、私はインターフェイスとwpfの新しいです... –

1

ルートディレクトリが基準を満たしていない場合は、ルーチンが直ちに終了するため、ディレクトリを少し異なるものにする必要があります。

ことは、これを試してください:あなたのScanItemを少し変更します。

public class ScanItem { 
    ... 
    public virtual bool IsDirectory { get; } 
    ... 
} 

あなたscanFileにこれを追加します。あなたのSCANDIRへ

public class ScanFile : ScanItem { 
    public override bool IsDirectory { 
    get { return false; } 
    } 
} 

と、この:

public class ScanDir : ScanItem { 
    public List<ScanItem> Items { get; set; } 
    public ScanDir() { 
    Items = new List<ScanItem>(); 
    } 

    public ScanDir CopyWithoutChildren() { 
    return new ScanDir() { 
     FullPath = this.FullPath, 
     ModifiedDate = this.ModifiedDate, 
     CreatedDate = this.CreatedDate, 
     Attributes = this.Attributes, 
     Size = this.Size 
    }; 
    } 

    public override bool IsDirectory { 
    get { return true; } 
    } 
} 

は今フィルタリングを行いますファイル上で空のディレクトリを省略します。

public List<ScanItem> search(List<ScanItem> items) { 
    var filter = new List<ScanItem>(); 

    foreach(var item in items) { 
    if(item.IsDirectory) { 
     List<ScanItem> potential = search(((ScanDir)item).Items); 
     if(potential.Count > 0) { 
     ScanDir dir = ((ScanDir)item).CopyWithoutChildren(); 
     dir.Items.AddRange(potential); 
     filter.Add(dir); 
     } 
    } else { 
     if(!item.FullPath.Contains("stringIwantToLookFor")) continue; 
     filter.Add(item); 
    } 
    } 

    return filter; 
} 

私はそれをテストしませんでしたが、私はあなたが望むことをするべきだと思います。

0

だから私はfooを含むファイルを探しているなら、このメソッドはfooを含むファイルを 'newList'リストに入れます。そのメソッドを呼び出す前に、そのリストを新しいリストと同じに設定する必要があります。私は明らかにパラメータのfooを変更するなどの基本的な実装が欠落しています。私は空のディレクトリを削除するために欠けています。

private List<ScanDir> history = new List<ScanDir>(); 
    private ScanDir LastDir; 
    private List<ScanItem> newList = new List<ScanItem>(); 
    public void Search(List<ScanItem> allItems) //adds files that contain foo 
    { 
     bool updateLastDir = false; 

     foreach(ScanItem s in allItems) 
     { 
      if (updateLastDir) 
      { 
       history = (from a in history 
          select a).Distinct().ToList(); 

       LastDir = null; 
       for (int i = history.Count - 1; i >= 0; i--) 
       { 
        if (history[i].FullPath == Directory.GetParent(s.FullPath).ToString()) 
        { 
         LastDir = history[i]; 
         break; 
        }       
       } 

       updateLastDir = false;           
      } 
      if (s.IsDirectory) 
      { 
       var temp = new ScanDir { FullPath = s.FullPath, IsDirectory = true, comparePath = s.comparePath, Attributes = s.Attributes }; 

       if (LastDir == null) 
       { 
        newList.Add(temp);       
       } 
       else 
       { 
        LastDir.Items.Add(temp); 

       } 

       LastDir = temp; 
       history.Add(LastDir); 

       Search(((ScanDir)s).Items); 

       history.RemoveAt(history.Count - 1); 

       updateLastDir = true; 


      } 
      else 
      { 
       if (s.Name.Contains("Foo")) // then add it 
       { 
        if (LastDir == null)       
         newList.Add(s);       
        else       
         LastDir.Items.Add(s);      
       } 
      } 
     } 

    }