Is this an abuse of 'dynamic'?
Is the following use of 'dynamic', in the method IsUnixNewline, goo开发者_C百科d or bad?
using System;
class Program
{
static void Main()
{
byte[] bytes = { 32, 32, 32, 10 };
string text = "hello\n";
for (int i = 0; i < bytes.Length; ++i) {
if (IsUnixNewline(bytes, i)) {
Console.WriteLine("Found Unix newline in 'bytes'.");
break;
}
}
for (int i = 0; i < text.Length; ++i) {
if (IsUnixNewline(text, i)) {
Console.WriteLine("Found Unix newline in 'text'.");
break;
}
}
}
static bool IsUnixNewline(dynamic array, int index)
{
return array[index] == '\n' && (index == 0 || array[index - 1] != '\r');
}
}
I would say "yes" - dynamic
is not required here, and adds a lot of uncertainty about what it will do at runtime (and, of course, throws away static compiler checking); better to just use a few overloads in this case, IMO:
static bool IsUnixNewline(char[] array, int index)
{
return array[index] == '\n' && (index == 0 || array[index - 1] != '\r');
}
static bool IsUnixNewline(byte[] array, int index)
{
return array[index] == '\n' && (index == 0 || array[index - 1] != '\r');
}
static bool IsUnixNewline(string array, int index)
{
return array[index] == '\n' && (index == 0 || array[index - 1] != '\r');
}
rewritten (without a compiler!) to make use of char[]
instead of dynamic
.
You need to take care of the correct encoding, when converting byte[]
to string
, but you should get the idea.
using System;
class Program
{
static void Main()
{
byte[] bytes = { 32, 32, 32, 10 };
string text = "hello\n";
char[] characterArray = System.Text.Encoding.ASCII.GetString(bytes).ToCharArray();
for (int i = 0; i < characterArray.Length; ++i) {
if (IsUnixNewline(characterArray, i)) {
Console.WriteLine("Found Unix newline in 'bytes'.");
break;
}
}
characterArray = text.ToCharArray();
for (int i = 0; i < characterArray .Length; ++i) {
if (IsUnixNewline(characterArray, i)) {
Console.WriteLine("Found Unix newline in 'text'.");
break;
}
}
}
static bool IsUnixNewline(char[] array, int index)
{
return array[index] == '\n' && (index == 0 || array[index - 1] != '\r');
}
}
In my opinion bad, as this
var b = IsUnixNewline(new NewObjectNotSupportingIndexer(), 0);
will go through the compiler perfectly fine during development, but fail during runtime. Why sacrificing type safety when you don´t have too? Dynamic is of great help if you don´t know the type during developemt (ComInterop), but in this case I think it causes more damage than help.
the dynamic keyword causes runtime type checking instead of compiletime type checking. I cannot for the life of me see why this simple operation explicitly requires runtime type checking. the same could very easily be achieved without dynamic. I don't know if I'd classify this as an abuse of dynamic, I'd certainly classify it as unnecessary.
First it's important to recognize that a byte
is not a character. Therefore, to convert those you need to first apply an encoding (even if that's only the ASCII
or Default
encoding). Doing that with the GetString
method of Encoding
will give you a string, making the distinction between the two types void.
However, to answer more generally, you should always try and see what the types have in common. For instance, both the char[]
and string
implement IEnumerable<char>
. Therefore, instead of checking each position for being a unit newline, why not have a method which checks all characters using an enumerator?
using System;
using System.Text;
using System.Collections.Generic;
class Program {
static void Main() {
byte[] bytes = { 32, 32, 32, 10 };
string text = "hello\n";
if (HasUnixNewline(Encoding.ASCII.GetChars(bytes))) {
Console.WriteLine("Found Unix newline in 'bytes'.");
}
if (HasUnixNewline(text)) {
Console.WriteLine("Found Unix newline in 'text'.");
}
}
static bool HasUnixNewline(IEnumerable<char> chars) {
bool prevIsCR = false;
foreach (char ch in chars) {
if ((ch == '\n') && (!prevIsCR)) {
return true;
}
prevIsCR = ch == '\r';
}
return false;
}
}
The other variant would be to use the so-called Adapter Pattern, that is, an (often stateless) object wrapping the original instance and providing alternative interfaces. In this case, you could easily implement an interface which exposes just the read indexer and returns characters, and then use this in your IsUnixNewLine
method. Or you could apply that same pattern to avoid the use of the Encoding
class in my sample by writing an adapter which implements IEnumerable<char>
and wraps an IEnumerable<byte>
.
精彩评论