2010-08-21 83 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); 
    ... 
} 
相关问题