也许这个问题已被问过百万次,但我找不到类似的话题。是否有可能编写一个具有多个'where'-s的泛型类,它将在编译时检查?这就是我今天所拥有的
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的人,并且给定接口的函数原型是相同的,所以它会很好让它以这种方式工作。
或者可能还有另一种简化当前代码的方法吗?
谢谢!
答案 0 :(得分: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);
...
}
答案 1 :(得分: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