2010-08-21 3 views
0

Возможно, этот вопрос уже задавался миллион раз, но я не мог найти подобную тему. Можно ли написать общий класс с несколькими «где», которые будут проверяться во время компиляции? Вот что я сегодняВозможно ли иметь 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

+0

Целью является возможность перечисления через экземпляр COM-интерфейсов IEnumBackgroundCopyFiles или IEnumBackgroundCopyJobs с использованием «foreach». Первая реализация - это то, что используется сегодня в производстве. Пример использования: IEnumBackgroundCopyFiles temp; job.EnumFiles (out temp); var files = new MsBitsEnumWrapper (temp); foreach (файл IBackgroundCopyFile в файлах) {} ::: Здесь мне нужно проверить время выполнения (в конструкторе), если данный параметр является экземпляром того или иного типа путем кастинга через «как». Я хотел бы иметь более общее решение. – expert

+0

ruslan, вы включаете слишком много неуместных деталей. Кроме того, пожалуйста, не пытайтесь вставить информацию в комментарий. Пожалуйста, отредактируйте вопрос, удалите все ненужные детали и код и задайте * сам вопрос * только с информацией, которая является * релевантной *, но, конечно, включая * все * соответствующую информацию. – Timwi

ответ

1

Как указано в JaredPar, вы не можете сделать это на C#. Выбор заключается в использовании делегатов или в старой школе с использованием абстрактного базового класса.

В моей реализации ниже используется «доходность возврата», чтобы сократить реализацию 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

1

Это похоже на то, что вы просите, если C# поддерживает структурную типизацию в generics так же, как C++ для шаблонов. Ответ №

C++ шаблоны не являются полностью оценены для правильности, пока функции-члены не используются с данным общим экземпляром. С другой стороны, генераторы C# полностью оцениваются самостоятельно, когда они компилируются. Это не оставляет места для структурного/совпадения по типу ввода.

Существует несколько подходов, которые вы можете предпринять здесь. Более трудоемкий подход заключается в создании другого интерфейса для функций GetCount и Reset. Затем создайте типы оберток для двух интерфейсов для подключения этих методов. Это немного утомительно, хотя через некоторое время.

Мое личное предпочтение заключается в решении этой проблемы с делегатами.

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

    uint count = getCount(_iface); 
    reset(_iface); 
    ... 
} 
Смежные вопросы