Использование типов generi c в цикле foreach - PullRequest
0 голосов
/ 28 мая 2020

Я действительно не могу понять, как использовать типы generi c с IEnumerable, чтобы я мог перебирать значения, содержащиеся в данном значении generi c.

Рассмотрим следующий класс (обратите внимание, что классы здесь только для примера):

public class Parameter<T> : IParameter<T> where T : IEnumerable<T>
{
  public List<UInt64> output = new List<UInt64>();

   private T _value;
   public T Value
   {
       get => ...;
       set
       {

           // I want to be able to apply special treat to the value
           // Value can be of any type: int, int[], bool, bool[]
           foreach (var v in value)
           {
               output.Add(Convert.UInt64(v) + 5);
           }

           ...

       }
   }
}

public interface IParameter<T> where T : IEnumerable<T>
{
   T Value { get; set; }
}

Затем у меня есть тестовый модуль, который создает некоторые параметры согласно, но я даже не могу скомпилировать Вот. Я даже пытался заменить bool [] на IEnumerable здесь, ниже, но компилятору это тоже не нравится.

public class TestModule : ModuleBase, ITestModule
{
    public IParameter<bool[]> Test1 { get; set; } = new Parameter<bool[]>();
    public IParameter<uint[]> Test2 { get; set; } = new Parameter<uint[]>();
    ...
    public IParameter<int> Test3 { get; set; } = new Parameter<int>();
}

Я действительно рассматривал возможность использования перегрузки для класса Parameter (), но счел излишним создавать класс для каждого поддерживаемого типа (учитывая, что это только для свойства Value).

1 Ответ

1 голос
/ 28 мая 2020

Ваша проблема в том, что ваш общий параметр c указан неправильно.

public class Parameter<T> : IParameter<T> where T : IEnumerable<T>

подразумевает, что все, что приходит с типом T, является перечислимым того же типа, то есть, например, T типа bool[] должен быть IEnumerable<bool[]>, что явно неверно.

Один из способов скомпилировать его:

    public class Parameter<TEnumerable, TType> : IParameter<TEnumerable, TType> where TEnumerable : IEnumerable<TType>
    {
        public List<ulong> output = new List<ulong>();

        private TEnumerable _value;
        public TEnumerable Value
        {
            get => { return null; }
            set
            {

                // I want to be able to apply special treat to the value
                // Value can be of any type: int, int[], bool, bool[]
                foreach (Q v in value)
                {
                    output.Add(Convert.ToUInt64(v) + 5);
                }
            }
        }
    }

    public interface IParameter<TEnumerable, TType> where TEnumerable : IEnumerable<TType>
    {
        TEnumerable Value { get; set; }
    }

    public class TestModule
    {
        public IParameter<bool[], bool> Test1 { get; set; } = new Parameter<bool[], bool>();
        public IParameter<uint[], uint> Test2 { get; set; } = new Parameter<uint[], uint>();
        public IParameter<int[], int> Test3 { get; set; } = new Parameter<int[], int>();
    }

Что касается вашего дополнительного комментария, нет, вы никак не можете избежать необходимости указывать два типа, поскольку IEnumerable является а не буква T в том виде, в котором вы сформулировали свой код. Здесь у вас есть 2 отдельных параметра, и поэтому вам придется использовать 2 параметра generi c, если вы должны сделать это так, как вы это сделали.

Гораздо более простым решением вашей проблемы было бы что-то вроде этого, которое более или менее служит той же цели, хотя я действительно не знаю ваших требований, поэтому этого может или не хватить (интерфейс опущен для ясности):

    public class Parameter<TType>
    {
        public List<ulong> output = new List<ulong>();

        private IEnumerable<TType> _value;
        public IEnumerable<TType> Value
        {
            get => { return null; }
            set
            {

                // I want to be able to apply special treat to the value
                // Value can be of any type: int, int[], bool, bool[]
                foreach (TType v in value)
                {
                    output.Add(Convert.ToUInt64(v) + 5);
                }
            }
        }
    }

    public class TestModule
    {
        public Parameter<bool> Test1 { get; set; } = new Parameter<bool>();
        public Parameter<uint> Test2 { get; set; } = new Parameter<uint>();
        public Parameter<int> Test3 { get; set; } = new Parameter<int>();
    }
...