Насколько я знаю, класс 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))
{
...
}
Общее разрешение перегрузки не учитывает ограничения, поэтому версия Add
считается применимой, выводя T = int
.
Оба метода применимы, и ни один из них определенно не лучше другого, поскольку нет преобразования между IEnumerable
и IFoo
. Хотя универсальные методы считаются «менее специфичными», чем неуниверсальные методы, это становится актуальным только тогда, когда типы параметров идентичны после замены аргумента типа, а в данном случае это не так.
В FooContainer при втором «Добавить» вы ограничиваете T типом IFoo. BigFoo реализует интерфейс IFoo, поэтому он как бы совпадает с определением Add (хотя на самом деле это не так, потому что он не реализует IEnumable
Я не уверен, что полностью понимаю, что вы хотите, но Я подозреваю, что это:
public void Add<T>(T group) where T : IEnumerable<IFoo> { }
, который позволит вам добавить любой объект T, где T - это перечислимый набор объектов IFoo.
Это то, что вы хотели?
С уважением, Ричард
Проблема в том, что ограничения универсального типа полностью игнорируются компилятором (он смотрит только на типы параметров). Что касается компилятора, то переданный аргумент IEnumerable
может также быть IEnumerable
.
Полную информацию по этому вопросу см. в раздел 25.6.4 Вывод аргументов типа в Спецификации языка C # . Обратите внимание, что здесь нет упоминания об использовании ограничений типа.
Интересно .... Только что попробовал свой образец. Дженерики продолжают держать меня в напряжении.
//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 { }
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);