Итерация по общему списку с несколькими типами

У меня есть 3 класса, как указано ниже: у одного есть информация для удаления, у остальных двух классов есть фактические данные. В будущем будет более 30 классов для данных

public class RemovalInformation<T> where T:class
{
    public string TagName { get; set; }
    public T Data { get; set; }
    public Func<T, bool> RemovalCondition { get; set; }
}

public class PropertyReportData
{
    public string PropertyName { get; set; }
}

public class ValuationData
{
    public DateTime ValuationDate { get; set; }
}

У меня есть ниже ArrayList, который я хочу обработать

        var removals = new ArrayList
        {
            new RemovalInformation<PropertyReportData>
            {
                Data = commercialReportData?.PropertyDetail,
                TagName = nameof(PropertyReportData.PropertyName),
                RemovalCondition = property => string.IsNullOrWhiteSpace(property.PropertyName),
            },
             new RemovalInformation<ValuationData>
            {
                Data = commercialReportData?.ValuationData,
                TagName = nameof(ValuationData.ValuationDate),
                RemovalCondition = property => property.ValuationDate< DateTime.Today,
            }
        };

        ProcessRemovals(removals);

Метод ProcessRemovals

    private void ProcessRemovals(ArrayList removals)
    {
        foreach (RemovalInformation<PropertyReportData> item in removals)
        {
            var deleteItem = item.RemovalCondition.Invoke(item.Data);
            if (deleteItem)
            {
               //do something here
            }
        }
    }

Проблема здесь в том, что в цикле foreach я могу получить доступ к RemovalInformation только одного типа. Есть ли способ перебрать список для нескольких типов RemovalInformation?


person M.S.    schedule 08.08.2019    source источник
comment
Обычно вы создаете базовый класс, от которого наследуются PropertyReportData и ValuationData. Если вы этого не сделаете, базовым классом будет объект. И объект, безусловно, худшая вещь, которую вы могли бы даже использовать в качестве T/части T. Ниже вам нужно будет проверить, является ли текущий экземпляр одним из двух классов, а затем привести к нему.   -  person Christopher    schedule 08.08.2019
comment
Во-вторых, здесь нельзя использовать foreach. foreach использует перечислители, неявно созданные для любого массива, и они не позволяют изменять базовую коллекцию. Если вы хотите изменить какую-либо коллекцию, foreach — это не путь.   -  person Christopher    schedule 08.08.2019
comment
В будущем будет более 30 классов для данных, на данный момент я упомянул только два класса.   -  person M.S.    schedule 08.08.2019
comment
Я не хочу ничего удалять из коллекции, поэтому foreach здесь не проблема.   -  person M.S.    schedule 08.08.2019


Ответы (3)


Вы можете использовать интерфейс, что-то вроде этого:

public interface IProcessRemoval
{
   bool Execute();
}

Просто реализуйте это:

public class RemovalInformation<T> : IProcessRemoval where T:class
{
    public string TagName { get; set; }
    public T Data { get; set; }
    public Func<T, bool> RemovalCondition { get; set; }
    public bool Execute()
    {
        if (RemovalCondition != null) 
        {
            return RemovalCondition(Data);
        }
        return false;
    }
}

Затем повторите:

private void ProcessRemovals(ArrayList removals)
{
    foreach (IProcessRemoval item in removals)
    {
        var deleteItem = item.Execute();
        if (deleteItem)
        {
           //do something here
        }
    }
}
person mtanksl    schedule 08.08.2019

Как ответил @mtanksl, чтобы иметь доступ к общим свойствам/методам, общим для всех типов, вам нужно будет определить интерфейс (или абстрактный класс). но для определенных типов вы можете использовать сопоставление шаблонов С# 7 (и выше):

private static void ProcessRemovals(ArrayList removals)
{
    foreach (var r in removals)
    {
         switch(r)
         {
             case RemovalInformation<PropertyReportData> propertyReportData:
                 //do delete with propertyReportData
                 break;
             case RemovalInformation<ValuationData> valuationData:
                 //do delete with valuationData
                 break;
             default:
                 var removalInfo = (IRemovalInformation)r;
                 //do delete with removalInfo that should holds all the common properties and methods
                 break;
         }

    }
}
person CountOren    schedule 08.08.2019
comment
По одному комментарию у него план до 30 занятий, так что swtich/case не подойдет. - person Christopher; 08.08.2019
comment
Ему не нужно покрывать все 30 дел. Если у него есть данные другой формы (разные свойства и/или методы), которые не используют один и тот же шаблон с остальными, то нет смысла группировать их в один и тот же интерфейс, чтобы он мог использовать сопоставление шаблонов для этих конкретных случаев. - person CountOren; 08.08.2019

В качестве альтернативы вы можете использовать тип dynamic:

private void ProcessRemovals(ArrayList removals)
{
    foreach (dynamic item in removals)
    {
        var deleteItem = item.RemovalCondition.Invoke(item.Data);
        if (deleteItem)
        {
           //do something here
        }
    }
}
person Krzysztof    schedule 18.08.2019