Estoy leyendo el código fuente de Avalonia y encontré esta oración:
return new MenuFlyoutPresenter { [!ItemsControl.ItemsProperty] = this[!ItemsProperty], [!ItemsControl.ItemTemplateProperty] = this[!ItemTemplateProperty] };
Nunca he visto una sintaxis como esa. ¿Qué hacen esos corchetes si no hay una propiedad indexada o este [] descriptor de acceso? ¿Y por qué se niegan con el signo de exclamación si la propiedad a la que se refieren no es un bool? ¿Quizás algún tipo de verificación nula?
El código en sí está contenido en el siguiente archivo cs:
https://github.com/AvaloniaUI/Avalonia/blob/master/src/Avalonia.Controls/Flyouts/MenuFlyout.cs
Rastreé el código pero no pude entender qué hace esa sintaxis.
Hay un par de cosas sucediendo aquí.
Primero, la sintaxis:
var menu = new MenuFlyoutPresenter { [key] = value, };
Es un inicializador de colección y es una abreviatura de:
var menu = new MenuFlyoutPresenter(); menu[key] = value;
Ese indexador se define aquí como:
public IBinding this[IndexerDescriptor binding] { get { return new IndexerBinding(this, binding.Property!, binding.Mode); } set { this.Bind(binding.Property!, value); } }
Entonces, la key
allí es un IndexerDescriptor
y el value
es un IBinding
.
Entonces, ¿qué está pasando con esta cosa?
!ItemsControl.ItemsProperty
Podemos ver en su enlace que ItemsProperty
es DirectProperty<TOwner, TValue>
y que, en última instancia, implementa el !
operador aquí :
public static IndexerDescriptor operator !(AvaloniaProperty property) { return new IndexerDescriptor { Priority = BindingPriority.LocalValue, Property = property, }; }
A Avalonia parece gustarle sobrecargar operadores como !
y ~
para hacer cosas que no esperarías (y para las que normalmente usarías un método). En este caso, utilizan !
en una AvaloniaProperty
como abreviatura para acceder al enlace de esa propiedad.
Una clase relativamente simple que demuestra una forma de permitir esta sintaxis es:
public sealed class Demo { public Demo this[Demo index] // Indexer { get => !index; set {} // Not needed to demonstrate syntax. } public static Demo operator !(Demo item) => item; public Demo ItemsProperty => _empty; public Demo ItemTemplateProperty => _empty; public Demo SomeMethod(Demo ItemsControl) { return new Demo { [!ItemsControl.ItemsProperty] = this[!ItemsProperty], [!ItemsControl.ItemTemplateProperty] = this[!ItemTemplateProperty], }; } static Demo _empty = new(); }
Algunas cosas a tener en cuenta:
operator!
implementos de Demo
! que permite el !
operador que se utilizará en valores de ese tipo (por ejemplo !ItemsProperty
en la inicialización de SomeMethod()
).Demo
implementa un indexador que es lo que permite el uso de la indexación (a través de this
) en el lado derecho del inicializador de la colección.[x] = y
utilizada en SomeMethod()
. ¡Es la combinación del operator()!
operador junto con el indexador this[Demo]
que habilita la sintaxis.