Игнорировать сериализацию модели в некоторых случаях [дубликат]

Что вы должны знать о this

this (иначе говоря, «контекст») - это специальное ключевое слово внутри каждой функции, и его значение зависит только от , как была вызвана функция, а не как / когда / где она была определена. Лексические области не затрагиваются, как и другие переменные. Вот несколько примеров:

function foo() {
    console.log(this);
}

// normal function call
foo(); // `this` will refer to `window`

// as object method
var obj = {bar: foo};
obj.bar(); // `this` will refer to `obj`

// as constructor function
new foo(); // `this` will refer to an object that inherits from `foo.prototype`

Чтобы узнать больше о this, просмотрите документацию MDN .


Как сделать обратитесь к правильному this

Не используйте this

Фактически вы не хотите иметь доступ к this в частности, но объект, на который он ссылается на . Вот почему простое решение - просто создать новую переменную, которая также относится к этому объекту. Переменная может иметь любое имя, но общие - self и that.

function MyConstructor(data, transport) {
    this.data = data;
    var self = this;
    transport.on('data', function() {
        alert(self.data);
    });
}

Поскольку self является нормальной переменной, она подчиняется лексическим правилам области и доступна внутри обратного вызова. Это также имеет то преимущество, что вы можете получить доступ к значению this самого обратного вызова.

Явно установить this обратного вызова - часть 1

Возможно, у вас есть не контролируйте значение this, потому что его значение устанавливается автоматически, но на самом деле это не так.

Каждая функция имеет метод .bind [docs] , который возвращает новую функцию с this, привязанную к значению. Функция имеет то же поведение, что и тот, который вы назвали .bind, только то, что this было установлено вами. Независимо от того, как и когда эта функция вызывается, this всегда будет ссылаться на переданное значение.

function MyConstructor(data, transport) {
    this.data = data;
    var boundFunction = (function() { // parenthesis are not necessary
        alert(this.data);             // but might improve readability
    }).bind(this); // <- here we are calling `.bind()` 
    transport.on('data', boundFunction);
}

В этом случае мы привязываем обратный вызов this к значению MyConstructor 's this.

Примечание. При связывании контекста для jQuery вместо этого используйте jQuery.proxy [docs] . Причина этого заключается в том, что вам не нужно сохранять ссылку на функцию при отмене обратного вызова события. jQuery обрабатывает это внутренне.

ECMAScript 6: Используйте функции стрелок

В ECMAScript 6 представлены функции стрелок , которые можно рассматривать как лямбда-функции. У них нет собственной привязки this. Вместо этого this просматривается в области видимости как обычная переменная. Это означает, что вам не нужно называть .bind. Это не единственное особое поведение, которое у них есть. Дополнительную информацию см. В документации MDN.

function MyConstructor(data, transport) {
    this.data = data;
    transport.on('data', () => alert(this.data));
}

Установите this обратного вызова - часть 2

Некоторые функции / методы, которые принимают обратные вызовы, также принимают значение, к которому должен обращаться обратный вызов this. Это в основном то же самое, что и привязывать его самостоятельно, но функция / метод делает это за вас. Array#map [docs] - такой метод. Его подпись такова:

array.map(callback[, thisArg])

Первый аргумент - это обратный вызов, а второй аргумент - значение this. Вот надуманный пример:

var arr = [1, 2, 3];
var obj = {multiplier: 42};

var new_arr = arr.map(function(v) {
    return v * this.multiplier;
}, obj); // <- here we are passing `obj` as second argument

Примечание. Можно ли передать значение для this, как правило, упоминается в документации этой функции / метода. Например, метод $.ajax jQuery [docs] описывает параметр, называемый context:

Этот объект станет контекстом всех обратных вызовов, связанных с Ajax.

blockquote>

Общая проблема: использование объектных методов в качестве обработчиков обратных вызовов / событий

Еще одно распространенное проявление этой проблемы - когда объектный метод используется как обработчик обратного вызова / события , Функции являются первоклассными гражданами в JavaScript, а термин «метод» - просто разговорный термин для функции, которая является значением свойства объекта. Но эта функция не имеет конкретной ссылки на ее «содержащий» объект.

Рассмотрим следующий пример:

function Foo() {
    this.data = 42,
    document.body.onclick = this.method;
}

Foo.prototype.method = function() {
    console.log(this.data);
};

Функция this.method назначается как обработчик события click , но если щелкнуть document.body, зарегистрированное значение будет undefined, потому что внутри обработчика события this ссылается на document.body, а не на экземпляр Foo. Как уже упоминалось в начале, то, что относится к [49], зависит от того, как называется функция, а не от того, как она определена. Если код выглядит следующим образом, может быть более очевидно, что функция не имеет неявной ссылки на объект:

function method() {
    console.log(this.data);
}


function Foo() {
    this.data = 42,
    document.body.onclick = this.method;
}

Foo.prototype.method = method;

Решение такое же, как указано выше: если доступно, используйте .bind явно привязать this к определенному значению

document.body.onclick = this.method.bind(this);

или явно вызвать функцию как «метод» объекта, используя анонимную функцию в качестве обработчика обратного вызова / события и назначить object (this) к другой переменной:

var self = this;
document.body.onclick = function() {
    self.method();
};

или использовать функцию стрелки:

document.body.onclick = () => this.method();
15
задан Alfred Myers 28 December 2015 в 14:04
поделиться

3 ответа

Вы должны использовать шаблон ShouldSerialize*:

public class Book
{
    [XmlAttribute] public string Title {get;set;}
    public bool ShouldSerializeTitle() {
        return !string.IsNullOrEmpty(Title);
    }

    [XmlAttribute] public string Description {get;set;}
    public bool ShouldSerializeDescription() {
        return !string.IsNullOrEmpty(Description );
    }

    [XmlAttribute] public string Author {get;set;}
    public bool ShouldSerializeAuthor() {
        return !string.IsNullOrEmpty(Author);
    }

    [XmlAttribute] public string Publisher {get;set;}
    public bool ShouldSerializePublisher() {
        return !string.IsNullOrEmpty(Publisher);
    }
}
33
ответ дан Marc Gravell 21 August 2018 в 11:54
поделиться
  • 1
    вы избили меня на 1 минуту;) – Thomas Levesque 4 November 2010 в 10:22
  • 2
    Я не понимал, как это решение работает до тех пор, пока я не прочитаю следующее сообщение: kjellsj.blogspot.com/2006/02/… – Dean 2 November 2011 в 17:59
  • 3
    +1 Самая крутая вещь, о которой я никогда не знала :) Просто решила особенно липкую проблему обратной совместимости с этим камнем. – Gone Coding 12 September 2013 в 10:17
  • 4
    Обратите внимание, что метод ShouldSerialize должен публиковаться здесь , в отличие от других случаев, таких как управление сериализации PropertyGrid, где метод может быть закрытым и не игнорировать. – Stelios Adamantidis 3 April 2014 в 18:13
public class Books
{
    [XmlElement("Book")]
    public List<Book> BookList;
}

public class Book
{
    [XmlAttribute]
    public string Title;
    [XmlAttribute]
    public string Description;
    [XmlAttribute]
    public string Author;
    [XmlAttribute]
    public string Publisher;
}

class Program
{
    static void Main()
    {
        var books = new Books
        {
            BookList = new List<Book>(new[] 
            {
                new Book 
                {
                    Title = "t1",
                    Description = "d1"
                },
                new Book 
                {
                    Author = "a2",
                    Description = "d2"
                },
                new Book 
                {
                    Author = "a3",
                    Title = "t3",
                    Publisher = "p3"
                },
            })
        };

        var serializer = new XmlSerializer(books.GetType());
        serializer.Serialize(Console.Out, books);
    }
}

И если вы хотите удалить пространство имен из корневого узла:

var namespaces = new XmlSerializerNamespaces();
namespaces.Add(string.Empty, string.Empty);
serializer.Serialize(Console.Out, books, namespaces);

Также я бы рекомендовал вам использовать свойства вместо полей / g1] в ваших классах моделей для лучшего инкапсуляции:

public class Books
{
    [XmlElement("Book")]
    public List<Book> BookList { get; set; }
}
1
ответ дан Darin Dimitrov 21 August 2018 в 11:54
поделиться
  • 1
    Действительно ли это отвечает на вопрос о пропуске нулевых / пустых элементов? – Will Dean 4 November 2010 в 10:00
  • 2
    @Will, нет, это не так ... – Thomas Levesque 4 November 2010 в 10:18
  • 3
    Действительно, мой код работает для нулевых элементов, но он не работает для пустых элементов (они все равно будут присутствовать в сгенерированном XML). – Darin Dimitrov 4 November 2010 в 11:41
  • 4
    Я полагаю, что когда вы используете свойства, по умолчанию значение равно null, и, следовательно, атрибуты не будут присутствовать в сгенерированном xml, если только они не будут установлены в пустое или любое другое значение. – user40907 4 November 2010 в 13:12
  • 5
    Поля также являются null по умолчанию для ссылочных типов и значением по умолчанию для типов значений. – Darin Dimitrov 4 November 2010 в 13:27

Альтернатива:

  • Переключить общедоступные поля в свойства
  • Определить значения по умолчанию с помощью атрибута DefaultValueAttribute
  • Определить свойство контента с помощью кнопки ContentPropertyAttribute attribute
  • Использовать XamlWriter / XamlReader

В итоге вы получите что-то вроде этого:

 [ContentProperty("Books")]
 public class Library {

   private readonly List<Book> m_books = new List<Book>();

   public List<Book> Books { get { return m_books; } }

 }

 public class Book
 {

    [DefaultValue(string.Empty)]
    public string Title { get; set; }

    [DefaultValue(string.Empty)]
    public string Description { get; set; }

    [DefaultValue(string.Empty)]
    public string Author { get; set; }

 }
6
ответ дан Nicolas Repiquet 21 August 2018 в 11:54
поделиться
  • 1
    Самое быстрое решение и с менее возможным шаблоном кода ... – Stelios Adamantidis 3 April 2014 в 18:26
  • 2
    Это должен быть ответ. ShouldSerialize * работает, но [DefaultValue (& quot;) "] является гораздо более простым способом – Ivan Krivyakov 14 July 2014 в 05:56
Другие вопросы по тегам:

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