C# универсальная перегрузка - Компилятор не может определить корректный вызов

Насколько я знаю, класс StringWriter всегда будет использовать кодировку UTF 16 при сериализации в строку. Вы можете написать свой собственный класс переопределения, который принимает другую кодировку:

public class StringWriterWithEncoding : StringWriter
{
    private readonly Encoding _encoding;

    public StringWriterWithEncoding()
    {
    }

    public StringWriterWithEncoding(IFormatProvider formatProvider)
        : base(formatProvider)
    {
    }

    public StringWriterWithEncoding(StringBuilder sb)
        : base(sb)
    {
    }

    public StringWriterWithEncoding(StringBuilder sb, IFormatProvider formatProvider)
        : base(sb, formatProvider)
    {
    }


    public StringWriterWithEncoding(Encoding encoding)
    {
        _encoding = encoding;
    }

    public StringWriterWithEncoding(IFormatProvider formatProvider, Encoding encoding)
        : base(formatProvider)
    {
        _encoding = encoding;
    }

    public StringWriterWithEncoding(StringBuilder sb, Encoding encoding)
        : base(sb)
    {
        _encoding = encoding;
    }

    public StringWriterWithEncoding(StringBuilder sb, IFormatProvider formatProvider, Encoding encoding)
        : base(sb, formatProvider)
    {
        _encoding = encoding;
    }

    public override Encoding Encoding
    {
        get { return (null == _encoding) ? base.Encoding : _encoding; }
    }
}

, так что вы можете использовать это вместо:

using(var stringWriter = new StringWriterWithEncoding( Encoding.UTF8))
{
   ...
}
5
задан Alexis 8 June 2009 в 15:21
поделиться

5 ответов

Общее разрешение перегрузки не учитывает ограничения, поэтому версия Add считается применимой, выводя T = int .

Оба метода применимы, и ни один из них определенно не лучше другого, поскольку нет преобразования между IEnumerable и IFoo . Хотя универсальные методы считаются «менее специфичными», чем неуниверсальные методы, это становится актуальным только тогда, когда типы параметров идентичны после замены аргумента типа, а в данном случае это не так.

6
ответ дан 14 December 2019 в 08:59
поделиться

В FooContainer при втором «Добавить» вы ограничиваете T типом IFoo. BigFoo реализует интерфейс IFoo, поэтому он как бы совпадает с определением Add (хотя на самом деле это не так, потому что он не реализует IEnumable ).

Я не уверен, что полностью понимаю, что вы хотите, но Я подозреваю, что это:

public void Add<T>(T group) where T : IEnumerable<IFoo> { }

, который позволит вам добавить любой объект T, где T - это перечислимый набор объектов IFoo.

Это то, что вы хотели?

С уважением, Ричард

1
ответ дан 14 December 2019 в 08:59
поделиться

Проблема в том, что ограничения универсального типа полностью игнорируются компилятором (он смотрит только на типы параметров). Что касается компилятора, то переданный аргумент IEnumerable может также быть IEnumerable .

Полную информацию по этому вопросу см. в раздел 25.6.4 Вывод аргументов типа в Спецификации языка C # . Обратите внимание, что здесь нет упоминания об использовании ограничений типа.

0
ответ дан 14 December 2019 в 08:59
поделиться

Интересно .... Только что попробовал свой образец. Дженерики продолжают держать меня в напряжении.

//1 - First preference
public void Add(BigFoo item) { Console.WriteLine("static BigFoo type Add"); }
//2 - Second Preference
public void Add<T>(T item) { Console.WriteLine("Generic Add");  }
//3 - Third preferences
public void Add(IFoo item) { Console.WriteLine("static IFoo interface Add"); }
//4 - Compiles if 1-4 exist. Compile error (ambiguity) if only 3-4 exist. Compile error (cannot convert int to IFoo) if only 4 exists
public void Add<T>(IEnumerable<T> group) where T : IFoo { }
0
ответ дан 14 December 2019 в 08:59
поделиться

The compiler should be smart enough to recognize that BigFoo can't be cast to IEnumerable, but it isn't. It simply sees that it's an IEnumerable, and feels that it's a potential overload candidate (even though the contstraint you defined enforces that T must be IFoo and int can't be cast to IFoo). While it's inconvenient, it's not that big of a deal. Just cast bigFoo to IFoo and the compiler will be happy:

fooContainer.Add((IFoo)bigFoo);

Alternately, you can make your generic overload of Add uglier:

public void Add<T, U>(U group)
    where T : IFoo
    where U : IEnumerable<T>
{
}

Either way, you have more typing, the second solution eliminates the need to cast calls to Add, but you will have to explicitly declare type on calls to the generic add (which ends up being more code:

fooContainer.Add<IFoo, IEnumerable<IFoo>>(enumerableFoo);
0
ответ дан 14 December 2019 в 08:59
поделиться
Другие вопросы по тегам:

Похожие вопросы: