Использование Struct as Generi c Параметр и приведение к базовому интерфейсу приводят к ошибке: должен быть тип значения, который не может иметь значение NULL - PullRequest
1 голос
/ 17 марта 2020

Мне нужно создать коллекцию экземпляров ConcreteProviderX для выполнения на нем операторов LINQ. Я предпочитаю использовать struct для типов ConcreteArgumentsX, но приведение к базовому интерфейсу IArguments при создании массива приводит к ошибке времени компиляции: тип 'IArguments' должен иметь тип, не допускающий значения NULL, чтобы использовать его в качестве параметра 'T' в тип или метод generi c «ICommandProvider» (код = CS0453). У меня есть альтернативная реализация с классом вместо struct для типов ConcreteArgumentsX, но это вызывает исключительную ситуацию System.InvalidCastException (невозможно преобразовать "SetProvider" в "ICommandProvider"). Как ошибка будет решаться для случая со структурой и для случая с классом?

namespace ErrorUsingStructForConcreteArgumentsX
{
    interface ICommandProvider<T> where T : struct, IArguments
    {
        void F(string command, out T? arguments);
    }

    class ConcreteProvider1 : ICommandProvider<ConcreteArguments1>
    {
        public void F(string command, out ConcreteArguments1? arguments) { arguments = null; /* other code */ }
    }

    class ConcreteProvider2 : ICommandProvider<ConcreteArguments2>
    {
        public void F(string command, out ConcreteArguments2? arguments) { arguments = null; /* other code */ }
    }

    interface IArguments { }
    struct ConcreteArguments1 : IArguments { /* some value type properties */ }
    struct ConcreteArguments2 : IArguments { /* some value type properties */ }

    class Program
    {
        static void Main(string[] args)
        {
            ICommandProvider<ConcreteArguments1> provider = new ConcreteProvider1(); // ok

            // compile-time error
            var providers = new ICommandProvider<IArguments>[]
            {
                (ICommandProvider<IArguments>)new ConcreteProvider1(),
                (ICommandProvider<IArguments>)new ConcreteProvider2()
            };
        }
    }
}

namespace AlternativeUsingClassForConcreteArgumentsX
{
    interface ICommandProvider<IArguments>
    {
        void F(string command, out IArguments arguments);
    }

    class ConcreteProvider1 : ICommandProvider<ConcreteArguments1>
    {
        public void F(string command, out ConcreteArguments1 arguments) { arguments = null; /* other code */ }
    }

    class ConcreteProvider2 : ICommandProvider<ConcreteArguments2>
    {
        public void F(string command, out ConcreteArguments2 arguments) { arguments = null; /* other code */ }
    }

    interface IArguments { }
    class ConcreteArguments1 : IArguments { /* some value type properties */  }
    class ConcreteArguments2 : IArguments { /* some value type properties */ }

    class Program
    {
        static void Main(string[] args)
        {
            ICommandProvider<ConcreteArguments1> provider = new ConcreteProvider1(); // ok

            // runtime error
            var providers = new ICommandProvider<IArguments>[]
            {
                (ICommandProvider<IArguments>)new ConcreteProvider1(),
                (ICommandProvider<IArguments>)new ConcreteProvider2()
            };
        }
    }
}

1 Ответ

2 голосов
/ 18 марта 2020

struct является типом значения, а IArguments является ссылочным типом. Тип ссылки - NULLable, однако тип значения - , а не NULLable. Поскольку общие ограничения c всегда равны "И", это означает, что ваш T должен удовлетворять всем условиям, которые записаны в вашем выражении where - where T : struct, IArguments. Так что это причина, по которой вы получили ошибку:

Тип 'IArguments' должен иметь тип значения, не допускающий значения NULL, чтобы использовать его в качестве параметра 'T' в общем * 1026. * тип или метод 'ICommandProvider'

ОБНОВЛЕНИЕ:

Может быть, эта реализация будет полезна:

static void Main(string[] args)
{
    ICommandProvider<IArguments> provider = new ConcreteProvider1();
    var providers = new ICommandProvider<IArguments>[]
    {
        new ConcreteProvider1(),
        new ConcreteProvider2()
    };
}

И другой код выглядит как это:

interface ICommandProvider<T> where T : IArguments
{
    void F(string command, T arguments);
}

class ConcreteProvider1 : ICommandProvider<IArguments>
{
    public void F(string command, IArguments arguments)
    {
        throw new NotImplementedException();
    }
}

class ConcreteProvider2 : ICommandProvider<IArguments>
{
    public void F(string command, IArguments arguments)
    {
        throw new NotImplementedException();
    }
}

interface IArguments { }
class ConcreteArguments1 : IArguments { /* some value type properties */ }
class ConcreteArguments2 : IArguments { /* some value type properties */ }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...