2010-08-21 82 views
0

也許這個問題已經被問過百萬次了,但是我找不到類似的話題。是否有可能寫一個泛型類與多個'where'-s將在編譯時檢查?這是我今天是否有可能在C#中使用類似於C++的泛型?

public class MsBitsEnumWrapper<TC, TE> : IEnumerable<TC> 
{ 
    internal class Helper : IEnumerator<TC> 
    { 
     private readonly TC[] _data; 
     private int _pos = -1; 
     private readonly IEnumBackgroundCopyJobs _jobs; 
     private readonly IEnumBackgroundCopyFiles _files; 

     // I miss C++ templates that allows me to implements this method in completelly generic fashion... 
     public Helper(TE source) 
     { 
      _jobs = source as IEnumBackgroundCopyJobs; 
      _files = source as IEnumBackgroundCopyFiles; 

      uint count = 0; 
      if (null != _jobs) 
      { 
       _jobs.GetCount(out count); 
       _jobs.Reset(); 
      } 
      else 
       if (null != _files) 
       { 
        _files.GetCount(out count); 
        _files.Reset(); 
       } 

      _data = new TC[count]; 

      for (uint i = 0; i < count; ++i) 
      { 
       uint fetched = 0; 
       if (null != _jobs) 
       { 
        IBackgroundCopyJob job; 
        _jobs.Next(1, out job, ref fetched); 
        _data[i] = (TC)job; 
       } 
       else 
        if (null != _files) 
        { 
         IBackgroundCopyFile file; 
         _files.Next(1, out file, ref fetched); 
         _data[i] = (TC)file; 
        } 
      } 
     } 

#region Implementation of IDisposable 
     public void Dispose() { } 
#endregion 

#region Implementation of IEnumerator 
     public bool MoveNext() 
     { 
      if (_pos < (_data.Length - 1)) 
      { 
       _pos++; 
       return true; 
      } 

      return false; 
     } 

     public void Reset() 
     { 
      _pos = -1; 
     } 

     public TC Current 
     { 
      get { return _data[_pos]; } 
     } 

     object IEnumerator.Current 
     { 
      get { return Current; } 
     } 
#endregion 
    } 

    private readonly Helper _enumerator; 

    public MsBitsEnumWrapper(TE source) { _enumerator = new Helper(source); } 

#region Implementation of IEnumerable 
    public IEnumerator<TC> GetEnumerator() { return _enumerator; } 
    IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } 
#endregion 
} 

很顯然,我不喜歡它,因爲我必須在運行時間檢測的通用參數的類型開關。有沒有可能有這樣的事情?

public class MsBitsEnumWrapper<TC, TE> : IEnumerable<TC> 
     where TE : IEnumBackgroundCopyJobs, IEnumBackgroundCopyFiles 
     where TC : IBackgroundCopyJob, IBackgroundCopyFile 
    { 
     internal class Helper : IEnumerator<TC> 
     { 
      private readonly TC[] _data; 
      private int _pos = -1; 
      private readonly TE _iface; 

      // I miss C++ templates that allows me to implements this method in completelly generic fashion... 
      public Helper(TE source) 
      { 
       _iface = source; 

       uint count; 
       _iface.GetCount(out count); 
       _iface.Reset(); 

       _data = new TC[count]; 

       for (uint i = 0; i < count; ++i) 
       { 
        uint fetched = 0; 
        TC job; 
        _iface.Next(1, out job, ref fetched); 
        _data[i] = job; 
       } 
      } 

#region Implementation of IDisposable 
      public void Dispose() { } 
#endregion 

#region Implementation of IEnumerator 
      public bool MoveNext() 
      { 
       if (_pos < (_data.Length - 1)) 
       { 
        _pos++; 
        return true; 
       } 

       return false; 
      } 

      public void Reset() 
      { 
       _pos = -1; 
      } 

      public TC Current 
      { 
       get { return _data[_pos]; } 
      } 

      object IEnumerator.Current 
      { 
       get { return Current; } 
      } 
#endregion 
     } 

     private readonly Helper _enumerator; 

     public MsBitsEnumWrapper(TE source) { _enumerator = new Helper(source); } 

#region Implementation of IEnumerable 
     public IEnumerator<TC> GetEnumerator() { return _enumerator; } 
     IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } 
#endregion 
    } 

我意識到,如果通用與TE = IEnumBackgroundCopyJobs和TC = IBackgroundCopyFile定義它不會工作,但因爲我是一個誰定義給出接口的TE和TC和函數原型是相同的它很高興能以這種方式工作。

或者可能有另一種方法來簡化當前的代碼?

謝謝!

+1

你想達到什麼目的?您發佈的代碼以什麼特定方式不能滿足您的需求?發佈一個不起作用的*小*代碼示例,並指定您遇到的錯誤。 – Timwi 2010-08-21 02:32:28

+0

目標是能夠使用「foreach」枚舉COM接口IEnumBackgroundCopyFiles或IEnumBackgroundCopyJobs的實例。第一個實施是今天在生產中使用的。使用示例: IEnumBackgroundCopyFiles temp; job.EnumFiles(out temp); var files = new MsBitsEnumWrapper (temp); foreach(IBackgroundCopyFile file in files){} :::在這裏,我必須檢查運行時(在構造函數中)如果給定的參數是通過「as」投射的一個或另一個類型的實例。我想要更通用的解決方案。 – expert 2010-08-21 02:48:11

+0

魯斯蘭,你是包括太多不相關的細節。另外,請不要試圖將信息填入評論中。請編輯問題,刪除所有不必要的詳細信息和代碼,並且只詢問*相關*的信息*,但當然包括*所有*相關信息。 – Timwi 2010-08-21 03:05:59

回答

1

正如JaredPar指出的那樣,您不能在C#中執行此操作。選擇是使用代表,或者使用抽象基類去老派。

我在下面的實現中利用'yield return'來減少IEnumerable的實現。

#if USE_DELEGATES 
public class MsBitsEnum<T> : IEnumerable<T> 
{ 
    Action _reset; 
    Func<T> _next; 
    Func<int> _count; 

    public MsBitsEnum(Action reset, Func<T> next, Func<int> count) 
    { 
     _reset = reset; 
     _next = next; 
     _count = count; 
    } 

    public IEnumerator<T> GetEnumerator() 
    { 
     _reset(); 
     int count = _count(); 
     for (int i = 0; i < count; ++i) 
      yield return _next(); 
    } 

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 
    { 
     return GetEnumerator(); 
    } 
} 

public class MsBitsJobs : MsBitsEnum<IBackgroundCopyJob> 
{ 
    public MsBitsJobs(IEnumBackgroundCopyJobs jobs) 
     : base(() => jobs.Reset(), 
       () => 
       { 
        IBackgroundCopyJob job = null; 
        uint fetched = 0; 
        jobs.Next(1, out job, out fetched); 
        return fetched == 1 ? job : null; 
       }, 
       () => 
       { 
        uint count; 
        jobs.GetCount(out count); 
        return (int) count; 
       }) 
    { 
    } 
} 

public class MsBitsFiles : MsBitsEnum<IBackgroundCopyFile> 
{ 
    public MsBitsFiles(IEnumBackgroundCopyFiles files) 
     : base(() => files.Reset(), 
       () => 
       { 
        IBackgroundCopyFile file = null; 
        uint fetched = 0; 
        files.Next(1, out file, out fetched); 
        return fetched == 1 ? file : null; 
       }, 
       () => 
       { 
        uint count; 
        files.GetCount(out count); 
        return (int)count; 
       }) 
    { 
    } 
} 

#else // !USE_DELEGATES 

public abstract class MsBitsEnum<T> : IEnumerable<T> 
{ 
    protected abstract void Reset(); 
    protected abstract bool Next(out T item); 

    public IEnumerator<T> GetEnumerator() 
    { 
     T item; 
     Reset(); 
     while (Next(out item)) 
      yield return item; 
    } 

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 
    { 
     return GetEnumerator(); 
    } 
} 

public class MsBitsJobs : MsBitsEnum<IBackgroundCopyJob> 
{ 
    IEnumBackgroundCopyJobs _jobs; 

    protected override void Reset() 
    { 
     _jobs.Reset(); 
    } 

    protected override bool Next(out IBackgroundCopyJob job) 
    { 
     uint fetched; 
     _jobs.Next(1, out job, out fetched); 
     return fetched == 1; 
    } 

    public MsBitsJobs(IEnumBackgroundCopyJobs jobs) 
    { 
     _jobs = jobs; 
    } 
} 

public class MsBitsFiles : MsBitsEnum<IBackgroundCopyFile> 
{ 
    IEnumBackgroundCopyFiles _files; 

    protected override void Reset() 
    { 
     _files.Reset(); 
    } 

    protected override bool Next(out IBackgroundCopyFile file) 
    { 
     uint fetched; 
     _files.Next(1, out file, out fetched); 
     return fetched == 1; 
    } 

    public MsBitsFiles(IEnumBackgroundCopyFiles files) 
    { 
     _files = files; 
    } 
} 
#endif // !USE_DELEGATES 
+0

我會去與委託版本。非常感謝! – expert 2010-08-24 04:00:25

1

這聽起來像你要求的是,如果C#支持泛型中的結構類型化,就像C++爲模板所做的一樣。答案是否。

C++模板不是完全評估正確性,直到成員函數與給定的泛型實例一起使用。另一方面,C#泛型在編譯時會被完全評估。它沒有留下結構/匹配的姓名輸入空間。

您可以在這裏採取幾種方法。更復雜的方法是爲GetCount和Reset函數創建另一個接口。然後爲兩個接口創建包裝類型以插入這些方法。雖然過了一段時間,但這有些乏味。

我個人的偏好是與代表解決這個問題。

public Helper(TE source, Func<TE,count> getCount, Action<TE> reset) 
{ 
    _iface = source; 

    uint count = getCount(_iface); 
    reset(_iface); 
    ... 
} 
相關問題