упаковка на структурах при вызове ToString ()

Во-первых Вы не должны звонить ToCharArray, поскольку строка может уже быть индексирована как массив символов, таким образом, это сохранит Вас выделение.

следующая оптимизация должна использовать StringBuilder для предотвращения ненужных выделений (поскольку строки неизменны, связывание их делает копию строки каждым разом). Для дальнейшей оптимизации этого, мы задаем длину StringBuilder, таким образом, она не должна будет разворачивать свой буфер.

public string Reverse(string text)
{
    if (string.IsNullOrEmpty(text))
    {
        return text;
    }

    StringBuilder builder = new StringBuilder(text.Length);
    for (int i = text.Length - 1; i >= 0; i--)
    {
        builder.Append(text[i]);
    }

    return builder.ToString();
}

Редактирование: Данные о производительности

я протестировал эту функцию и функцию с помощью Array.Reverse со следующей простой программой, где Reverse1 одна функция, и Reverse2 другой:

static void Main(string[] args)
{
    var text = "abcdefghijklmnopqrstuvwxyz";

    // pre-jit
    text = Reverse1(text); 
    text = Reverse2(text);

    // test
    var timer1 = Stopwatch.StartNew();
    for (var i = 0; i < 10000000; i++)
    {
        text = Reverse1(text);
    }

    timer1.Stop();
    Console.WriteLine("First: {0}", timer1.ElapsedMilliseconds);

    var timer2 = Stopwatch.StartNew();
    for (var i = 0; i < 10000000; i++)
    {
        text = Reverse2(text);
    }

    timer2.Stop();
    Console.WriteLine("Second: {0}", timer2.ElapsedMilliseconds);

    Console.ReadLine();
}

оказывается, что для коротких строк Array.Reverse метод приблизительно вдвое более быстр, чем тот выше, и для более длинных строк различие является еще более явным. Таким образом, учитывая, что Array.Reverse метод и более прост и быстрее, я рекомендовал бы использовать это, а не этого. Я оставляю на виду этого здесь только, чтобы показать, что это не способ, которым необходимо сделать это (к моему большому удивлению!)

12
задан zebrabox 8 August 2009 в 17:30
поделиться

3 ответа

Если thisType является типом значения и thisType не реализует метод затем ptr разыменовывается, помещается в коробку и передается как указатель this на инструкция метода callvirt.

Последний случай может произойти, только если метод был определен на объекте , ValueType или Enum и не замещается Автор thisType . В этом случае бокс вызывает копию исходного объекта

Ответ - да, тип значения - в рамке . Вот почему всегда полезно переопределить ToString () в пользовательских структурах.

9
ответ дан 2 December 2019 в 18:20
поделиться

Изменить: ответ kek444 правильный. Приношу свои извинения за неправильное понимание вопроса. Я оставляю свой ответ здесь, поскольку считаю, что он имеет дополнительную ценность и актуальную информацию для будущих читателей.

Я также думаю, что эта цитата из ссылки в ответ Мехрдада особенно наводит на размышления. :

  • Если thisType является типом значения и thisType не реализует метод затем ptr разыменовывается, помещается в коробку и передается как указатель this на инструкция метода callvirt.

Последний случай может произойти только тогда, когда метод был определен на объекте, ValueType или Enum без переопределения по thisType. В этом случае бокс вызывает копию исходного объекта быть произведенным. Однако, поскольку ни один из методы Object, ValueType и Enum изменяет состояние объекта, этот факт не может быть обнаружен.

Следовательно, нельзя написать программу, чтобы продемонстрировать, что происходит бокс. Это можно различить только при взгляде на IL и полном понимании ограниченного префикса для инструкции callvirt .


Из раздела 11.3.5 спецификации языка C # на http : //download.microsoft.com/download/3/8/8/388e7205-bc10-4226-b2a8-75351c669b09/CSharp%20Language%20Specification.doc ( http://msdn.microsoft.com /en-us/vcsharp/aa336809.aspx):

Когда тип структуры переопределяет виртуальный метод, унаследованный от System.Object (например, Equals, GetHashCode или ToString), вызов виртуального метода через экземпляр типа struct не вызывает упаковки. Это верно, даже когда структура используется как параметр типа, а вызов происходит через экземпляр типа параметра типа. Например:

using System;
struct Counter
{
    int value;
    public override string ToString() {
        value++;
        return value.ToString();
    }
}
class Program
{
    static void Test<T>() where T: new() {
        T x = new T();
        Console.WriteLine(x.ToString());
        Console.WriteLine(x.ToString());
        Console.WriteLine(x.ToString());
    }
    static void Main() {
        Test<Counter>();
    }
}

Результатом программы является:

1
2
3

Хотя наличие побочных эффектов для ToString является плохим стилем, пример демонстрирует, что для трех вызовов x.ToString () не было упаковки.

Аналогично , бокс никогда не происходит неявно при доступе к члену по параметру ограниченного типа. Например, предположим, что интерфейс ICounter содержит метод Increment, который можно использовать для изменения значения. Если ICounter используется в качестве ограничения, реализация метода Increment вызывается со ссылкой на переменную, для которой был вызван Increment, а не на коробочную копию.

using System;
interface ICounter
{
    void Increment();
}
struct Counter: ICounter
{
    int value;
    public override string ToString() {
        return value.ToString();
    }
    void ICounter.Increment() {
        value++;
    }
}
class Program
{
    static void Test<T>() where T: ICounter, new() {
        T x = new T();
        Console.WriteLine(x);
        x.Increment();                      // Modify x
        Console.WriteLine(x);
        ((ICounter)x).Increment();      // Modify boxed copy of x
        Console.WriteLine(x);
    }
    static void Main() {
        Test<Counter>();
    }
}

Первый вызов Increment изменяет значение в переменной x. Это не эквивалентно второму вызову Increment, который изменяет значение в коробочной копии x. Таким образом, вывод программы:

0
1
1

Для получения дополнительных сведений о упаковке и распаковке см. §4.3.

7
ответ дан 2 December 2019 в 18:20
поделиться

Нет, это не упаковывается, когда вы вызываете ToString или GetHashCode , если это реализовано вашей структурой ( почему это должно быть? ограниченная инструкция IL позаботится об этом .) Она упакована, когда вы вызвать невиртуальный метод (или виртуальный метод, не переопределенный в структуре) для System.Object (его базовый класс), то есть GetType / MemberwiseClone .

ОБНОВЛЕНИЕ: Приносим извинения за недоразумение, которое оно могло вызвать. Я написал ответ с переопределением методов в структуре (поэтому я упомянул, что невиртуальные методы нуждаются в боксе, Я должен был быть более явным, чтобы не сбивать читателей с толку, тем более, что я пропустил ваше заявление о том, что метод не переопределяется), как будто вы его не переопределите, метод Object.ToString ожидает его первый аргумент (ссылка на this ) должен быть ссылочным типом (экземпляр Object ). Очевидно, что значение должно быть заключено в рамку в этом вызове (поскольку это вызов в базовом классе).

Однако суть в том, что природа вызова виртуального метода для типа значения не соответствует приводит к выдаче инструкции box (в отличие от невиртуальных методов в Object , которые всегда приводят к выдаче явной инструкции box ).

4
ответ дан 2 December 2019 в 18:20
поделиться
Другие вопросы по тегам:

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