Pregunta

Me encuentro con un problema inusual en mis pruebas unitarias.La clase que estoy probando crea una propiedad de dependencia dinámicamente en tiempo de ejecución y el tipo de esa propiedad de dependencia puede variar según las circunstancias.Mientras escribo mis pruebas unitarias, necesito crear la propiedad de dependencia con diferentes tipos y eso genera errores porque no se puede redefinir una propiedad de dependencia existente.

Entonces, ¿hay alguna forma de cancelar el registro de una propiedad de dependencia o de cambiar el tipo de una propiedad de dependencia existente?

¡Gracias!


OverrideMetadata() solo le permite cambiar muy pocas cosas, como el valor predeterminado, por lo que no es útil.El enfoque AppDomain es una buena idea y podría funcionar, pero parece más complicado de lo que realmente quería profundizar por el bien de las pruebas unitarias.

Nunca encontré una manera de cancelar el registro de una propiedad de dependencia, así que investigué y reorganicé cuidadosamente mis pruebas unitarias para evitar el problema.Estoy obteniendo un poco menos de cobertura de prueba, pero como este problema nunca ocurriría en una aplicación real y solo durante las pruebas unitarias puedo vivir con él.

¡Gracias por la ayuda!

¿Fue útil?

Solución

Tuve un problema similar ayer cuando intentaba probar mi propia clase de creación DependencyProperty. Encontré esta pregunta y noté que no había una solución real para cancelar el registro de las propiedades de dependencia. Así que hice algo de excavación usando Red Gate .NET Reflector para ver qué podría aparecer. con.

Mirando las sobrecargas DependencyProperty.Register, todas parecían apuntar a DependencyProperty.RegisterCommon. Ese método tiene dos porciones:

Primero para verificar si la propiedad ya está registrada

FromNameKey key = new FromNameKey(name, ownerType);
lock (Synchronized)
{
  if (PropertyFromName.Contains(key))
  {
    throw new ArgumentException(SR.Get("PropertyAlreadyRegistered", 
      new object[] { name, ownerType.Name }));
  }
}

Segundo, registrar la propiedad de dependencia

DependencyProperty dp = 
  new DependencyProperty(name, propertyType, ownerType, 
    defaultMetadata, validateValueCallback);

defaultMetadata.Seal(dp, null);
//...Yada yada...
lock (Synchronized)
{
  PropertyFromName[key] = dp;
}

Ambas piezas se centran alrededor de DependencyProperty.PropertyFromName, una tabla hash. También noté el DependencyProperty.RegisteredPropertyList, un ItemStructList<DependencyProperty> pero no he visto dónde se usa. Sin embargo, por seguridad, pensé que trataría de eliminarlo también de ser posible.

Así que terminé con el siguiente código que me permitió " cancelar el registro de " una propiedad de dependencia.

private void RemoveDependency(DependencyProperty prop)
{
  var registeredPropertyField = typeof(DependencyProperty).
    GetField("RegisteredPropertyList", BindingFlags.NonPublic | BindingFlags.Static);
  object list = registeredPropertyField.GetValue(null);
  var genericMeth = list.GetType().GetMethod("Remove");
  try
  {
    genericMeth.Invoke(list, new[] { prop });
  }
  catch (TargetInvocationException)
  {
    Console.WriteLine("Does not exist in list");
  }

  var propertyFromNameField = typeof(DependencyProperty).
    GetField("PropertyFromName", BindingFlags.NonPublic | BindingFlags.Static);
  var propertyFromName = (Hashtable)propertyFromNameField.GetValue(null);

  object keyToRemove = null;
  foreach (DictionaryEntry item in propertyFromName)
  {
    if (item.Value == prop)
      keyToRemove = item.Key;
  }
  if (keyToRemove != null)
  propertyFromName.Remove(keyToRemove);
}

Funcionó lo suficientemente bien como para ejecutar mis pruebas sin obtener un " AlreadyRegistered " excepción. Sin embargo, le recomiendo encarecidamente que no utilice esto en ningún tipo de código de producción. Es probable que haya una razón por la que MSFT eligió no tener una forma formal de anular el registro de una propiedad de dependencia e intentar ir en contra solo está pidiendo problemas.

Otros consejos

Si todo lo demás falla, puede crear un nuevo AppDomain para cada Prueba.

No creo que pueda anular el registro de una propiedad de dependencia, pero puede redefinirla anulando los metadatos de esta manera:

MyDependencyProperty.OverrideMetadata(typeof(MyNewType), 
                     new PropertyMetadata());

Si registramos el nombre de una etiqueta como esta:

Label myLabel = new Label();
this.RegisterName(myLabel.Name, myLabel);

Podemos dar de baja fácilmente el nombre usando:

this.UnregisterName(myLabel.Name);

Me enfrentaba a un escenario en el que creé un control personalizado que hereda de Selector que debe tener dos propiedades ItemsSource, HorizontalItemsSource y VerticalItemsSource.

Ni siquiera uso la propiedad ItemsControl y no quiero que el usuario pueda acceder a ella.

Entonces leí La gran respuesta de Statenjason., y me dio un gran punto de vista sobre cómo eliminar un DP.
Sin embargo, mi problema fue que desde que declaré el ItemsSourceProperty miembro y el ItemsSource como Private Shadows (private new en C#), no pude cargarlo en tiempo de diseño desde que usé MyControlType.ItemsSourceProperty se referiría a la variable sombreada.
Además, cuando se utiliza el bucle mencionado en la respuesta anterior (foreach DictionaryEntry etc.), se produjo una excepción que decía que la colección cambió durante la iteración.

Por lo tanto, se me ocurrió un enfoque ligeramente diferente en el que se hace referencia a DependencyProperty de forma rígida en tiempo de ejecución y la colección se copia en una matriz para que no se modifique (VB.NET, lo siento):

Dim dpType = GetType(DependencyProperty)
Dim bFlags = BindingFlags.NonPublic Or BindingFlags.Static

Dim FromName = 
  Function(name As String, ownerType As Type) DirectCast(dpType.GetMethod("FromName",
    bFlags).Invoke(Nothing, {name, ownerType}), DependencyProperty)

Dim PropertyFromName = DirectCast(dpType.GetField("PropertyFromName", bFlags).
  GetValue(Nothing), Hashtable)

Dim dp = FromName.Invoke("ItemsSource", GetType(DimensionalGrid))
Dim entries(PropertyFromName.Count - 1) As DictionaryEntry
PropertyFromName.CopyTo(entries, 0)
Dim entry = entries.Single(Function(e) e.Value Is dp)
PropertyFromName.Remove(entry.Key)

Nota IMPORTANTE: todo el código anterior está rodeado en el constructor compartido del control personalizado, y no tengo que verificar si está registrado, porque sé que una subclase de Selcetor SÍ proporciona eso ItemsSource dp.

Tuve un problema con un ContentPresenter con diferentes Datatemplates donde uno de ellos tenía una DependencyProperty con un PropertyChangedCallback Al cambiar el contenido de ContentPresenters a otro DataTemplate, la devolución de llamada se mantuvo.

En el evento descargado de UserControls llamé:

BindingOperations.ClearAllBindings(this);
Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Normal, new DispatcherOperationCallback(delegate { return null; }), null);

Eso funcionó para mí

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top