是否有可能在C#中使用C ++ - 类似泛型?

时间:2010-08-21 02:25:35

标签: c# .net generics

也许这个问题已被问过百万次,但我找不到类似的话题。是否有可能编写一个具有多个'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的人,并且给定接口的函数原型是相同的,所以它会很好让它以这种方式工作。

或者可能还有另一种简化当前代码的方法吗?

谢谢!

2 个答案:

答案 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