Общий явный сбой приведения C#

У меня есть некоторые проблемы со следующим фрагментом кода. Я хотел бы указать строку для объекта, это работает отлично, однако, если этот объект является частью универсального класса, это приводит к сбою со следующим исключением ошибки: «Невозможно преобразовать объект типа« System.String » ввести 'test.B'". Хотя я перегрузил метод.

using System;
using System.Collections.Generic;

namespace test {
    class Program {
        static void Main(string [] args) {
            // These two cast perfectly fine.
            B x = (B) "abc";
            C y = (C) "def";

            A <B> a = new A<B>();
            a.b();
            A <C> b = new A<C>();
            b.b();
        }
    }

    class A<T> {
        public List <T> a = new List<T>();

        public void b() {
            // Unable to cast object of type 'System.String' to type 'test.B'
            this.a.Add ((T) (object) "abc"); 
            this.a.Add ((T) (object) "def");
            this.a.Add ((T) (object) "ghi");
        }
    }

    class B {
        public string b;

        public static explicit operator B(string a) {
            B x = new B();
            x.b = a;
            return x;
        }
    }

    class C {
        public string c;

        public static explicit operator C(string a) {
            C x = new C();
            x.c = a;
            return x;
        }
    }
}

Было бы здорово, если бы кто-нибудь мог объяснить мне, почему это не происходит должным образом.

Спасибо


person Doren Roosje    schedule 13.08.2012    source источник


Ответы (4)


Операторы преобразования применяются только тогда, когда тип известен статически; в конце концов, универсальные методы должны использовать один и тот же IL для каждого T, поэтому в некоторых случаях он не может вызывать ваш оператор, а в других — проверку типа.

Кроме того, поскольку вы явно привели к object, оно никогда не будет использоваться; приведение из object — это всегда простая проверка распаковки или ввода.

Злое исправление было бы (и мне это не нравится):

        this.a.Add((T)(dynamic)"abc");
        this.a.Add((T)(dynamic)"def");
        this.a.Add((T)(dynamic)"ghi");

который откладывает разрешение до времени выполнения. Это работает, но мне нужно будет промыть глаза после этого. Однако в более общем плане: операторы и дженерики не очень хорошо сочетаются, поэтому постарайтесь не использовать эту комбинацию в своем API. Лично я действительно не стал бы использовать вышеперечисленное!

person Marc Gravell    schedule 13.08.2012
comment
Спасибо за быстрый ответ на мой вопрос, я ценю это. Я попытаюсь найти другой подход, так как после некоторых исследований динамического типа я согласен с вами, что это вовсе не лучший подход. Спасибо еще раз. - person Doren Roosje; 13.08.2012

Компилятор не знает во время компиляции, если общий параметр имеет явное приведение, которое вы используете.

Однако вы можете ввести интерфейс и установить ограничение на общий параметр.

person vittore    schedule 13.08.2012
comment
вы не можете помещать операторы преобразования в интерфейсы, хотя - person Marc Gravell; 13.08.2012
comment
@Marc, вы можете обернуть преобразование в метод - person vittore; 13.08.2012
comment
для преобразования в методы, возможно; convert from сложнее (это то, о чем идет речь) - вам нужно создать экземпляр типа для вызова метода, который создает экземпляры типа - но противно - person Marc Gravell; 13.08.2012
comment
ну, определенно нет способа указать конструктор, кроме как без параметров, в общем ограничении параметра, но комбинация ограничений where T: IMyInter,new() сделает свое дело. - person vittore; 13.08.2012

Вы вставили приведение (object), чтобы компилятор перестал говорить вам, что вы делаете это неправильно. Это сработало, компилятор больше не может жаловаться, потому что это лишило его возможности проверять типы. И приведение от объекта к T может действительно сработать, как бы ни были малы шансы.

Однако вы не учли, что операторы явного преобразования являются функцией языка С#. Только компилятор знает, какой метод выполнять при применении такого приведения. Это не функция CLR, среда выполнения не занимается поиском подходящего метода преобразования. В противном случае компилятор C# бессилен использовать какие-либо операторы, указанные вами во время компиляции, поскольку он не знает тип T.

Проблема в том, что компилятора больше нет рядом, чтобы помочь при выполнении приведения. Кабум.

Применение универсальных типов во время выполнения, а не во время компиляции, является функцией .NET, общий термин - «овеществленные дженерики». В отличие от «стирания типов», способ реализации дженериков в Java и C++. Проблема с удалением типа заключается в том, что вся информация об аргументах универсального типа теряется после компиляции кода, универсальный тип не может использоваться другим языком, а отражение не работает. Проблема с овеществленными дженериками заключается в том, что нельзя использовать операции, которые нельзя универсально применить к какому-либо типу. Как оператор+(). И нравится этот актерский состав.

person Hans Passant    schedule 13.08.2012

Это потому, что T для A<T> не знает о явных операторах приведения для B или C.

person Daniel A. White    schedule 13.08.2012