Как вы можете выбросить элементы в «ICollection», не зная его «TSource»? - PullRequest
0 голосов
/ 13 мая 2018

Как вы можете бросить предметы в ICollection, не зная его TSource?Я думал, что LINQ будет лучшим путем для этого, но я не могу понять, как это можно сделать с помощью Reflection или любого другого метода.

Я пытаюсь реализовать это в классе, который наследует System.Attribute, такой общийтипы не допускаются.И возвращаемая информация о типе должна совпадать с информацией о типе, полученной для параметра value в MutateValue.Очевидно, что в этом случае generic будет радостью, но есть другое решение.

using System;
using System.Collections;
using System.ComponentModel.DataAnnotations;
using System.Linq;

namespace Dado.ComponentModel.DataMutations
{
    [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
    public class ApplyMaxLengthStackAttribute : MutationAttribute
    {
        private int _maxLength = -1;

        public object Mutate(object value, int maxLength, IMutationContext context = null)
        {
            _maxLength = maxLength;

            return Mutate(value, context);
        }

        protected override object MutateValue(object value, IMutationContext context)
        {
            if (value != null) {
                if (
                    value is string valueAsString &&
                    (
                        _maxLength > -1 || TryGetStringLengthAttributeValue(context, out _maxLength)
                    ) &&
                    valueAsString.Length > _maxLength
                ) {
                    value = valueAsString.Substring(0, _maxLength);
                }
                else if (
                    value is ICollection valueAsCollection &&
                    (
                        _maxLength > -1 || TryGetMaxLengthAttributeValue(context, out _maxLength)
                    ) &&
                    valueAsCollection.Count > _maxLength
                ) {
                    // Error CS1061
                    value = valueAsCollection.Take(_maxLength);
                }
            }

            _maxLength = -1;

            return value;
        }

        private bool TryGetStringLengthAttributeValue(IMutationContext context, out int maxLength)
        {
            var attribute = context?.Attributes.OfType<StringLengthAttribute>().FirstOrDefault();

            if (attribute != null) {
                maxLength = attribute.MaximumLength;

                return true;
            }

            return TryGetMaxLengthAttributeValue(context, out maxLength);
        }

        private bool TryGetMaxLengthAttributeValue(IMutationContext context, out int maxLength)
        {
            var attribute = context?.Attributes.OfType<MaxLengthAttribute>().FirstOrDefault();

            if (attribute != null) {
                maxLength = attribute.Length;

                return true;
            }

            maxLength = -1;

            return false;
        }
    }
}

Вы увидите, что этот код не скомпилируется и выдает эту ошибку:

ОшибкаCS1061: «ICollection» не содержит определения для «Take», и метод расширения «Take», принимающий первый аргумент типа «ICollection», не найден (отсутствует директива using или ссылка на сборку?)

Этот класс является расширением проекта Dado.ComponentModel.Mutations .Определения для других Dado.ComponentModel.Mutations определений членов следует искать там.

Ответы [ 2 ]

0 голосов
/ 14 мая 2018

dynamic на помощь!

value = Enumerable.Take((dynamic)value, _maxLength);

Есть несколько веских причин для использования dynamic в языке с такой мощной системой типов, как C #, но у него есть свои случайные недостатки, когда разработчики языка по какой-либо причине (обычно нехватка времени / ресурсов) не делали ' t реализовать какой-то способ выражения чего-то, что должно быть допустимым для выражения и возможных для их реализации. Тем не менее, когда вы сталкиваетесь с тем, что кажется довольно произвольным ударом (или стеной), который является просто произвольным ограничением языка, я думаю, что это совершенно веская причина, чтобы кратко отказаться от системы типов, что в принципе и позволяет dynamic ты сделаешь. Я полагаю, что вы нарушили одно из этих произвольных ограничений, и вы вполне оправданно используете dynamic в этой ситуации.

Если вы хотите сделать свой код немного более «безопасным» или надежным (если какая-то коллекция, которая не реализует IEnumerable<>, будет передана), вы можете изменить другое, если хотите проверить, реализует ли значение IEnumerable<> (что вам, к сожалению, придется сделать с помощью размышлений)

0 голосов
/ 14 мая 2018

Многие методы расширения в LINQ работают с типом IEnumerable<T>, а не IEnumerable. Take является одним из таких методов. Попробуйте привести к IEnumerable<object> так:

valueAsCollection.Cast<object>().Take(_maxLength)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...