Domanda

Sto riscontrando un problema insolito nei test delle mie unità. La classe che sto testando crea dinamicamente una proprietà di dipendenza in fase di esecuzione e il tipo di quella proprietà di dipendenza può variare a seconda delle circostanze. Mentre scrivo i miei test unitari, devo creare la proprietà di dipendenza con tipi diversi e questo porta ad errori perché non è possibile ridefinire una proprietà di dipendenza esistente.

Quindi c'è un modo per annullare la registrazione di una proprietà di dipendenza o per cambiare il tipo di una proprietà di dipendenza esistente?

Grazie!


OverrideMetadata () ti consente di modificare solo poche cose come il valore predefinito, quindi non è utile. L'approccio AppDomain è una buona idea e potrebbe funzionare ma sembra più complicato di quanto volessi approfondire per motivi di unit test.

Non ho mai trovato un modo per annullare la registrazione di una proprietà di dipendenza, quindi ho puntato e riorganizzato con cura i test delle mie unità per evitare il problema. Sto ottenendo un po 'meno copertura del test, ma poiché questo problema non si verificherebbe mai in un'applicazione reale e solo durante i test unitari posso convivere con esso.

Grazie per l'aiuto!

È stato utile?

Soluzione

Ho avuto problemi simili proprio ieri quando ho provato a testare la mia classe di creazione di DependencyProperty. Mi sono imbattuto in questa domanda e ho notato che non c'era una vera soluzione per annullare la registrazione delle proprietà di dipendenza. Quindi ho scavato usando Red Gate .NET Reflector per vedere cosa avrei potuto inventare con.

Osservando i DependencyProperty.Register sovraccarichi, sembravano tutti indicare DependencyProperty.RegisterCommon. Tale metodo ha due parti:

Prima controlla se la proprietà è già registrata

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

In secondo luogo, la registrazione della proprietà Dependency

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

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

Entrambi i pezzi sono centrati intorno a DependencyProperty.PropertyFromName, una HashTable. Ho anche notato DependencyProperty.RegisteredPropertyList, ItemStructList<DependencyProperty> ma non ho visto dove viene utilizzato. Tuttavia, per sicurezza, ho pensato di provare a rimuoverlo anche se possibile.

Quindi ho finito con il seguente codice che mi ha permesso di " annullare la registrazione di " una proprietà di dipendenza.

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);
}

Ha funzionato abbastanza bene da eseguire i miei test senza ottenere un " AlreadyRegistered " eccezione. Tuttavia, ti consiglio vivamente di non utilizzarlo in nessun tipo di codice di produzione. È probabile che MSFT abbia scelto di non avere un modo formale di annullare la registrazione di una proprietà di dipendenza e di tentare di andare contro sta solo chiedendo problemi.

Altri suggerimenti

Se tutto il resto fallisce, puoi creare un nuovo AppDomain per ogni Test.

Non penso che tu possa annullare la registrazione di una proprietà di dipendenza, ma puoi ridefinirla sovrascrivendo i metadati in questo modo:

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

Se registriamo il nome di un'etichetta come questa:

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

Possiamo facilmente annullare la registrazione del nome usando:

this.UnregisterName(myLabel.Name);

Stavo affrontando uno scenario in cui ho creato un controllo personalizzato che eredita da Selector che dovrebbe avere due proprietà ItemsSource, HorizontalItemsSource e VerticalItemsSource.

Non utilizzo nemmeno la proprietà ItemsControl e non voglio che l'utente sia in grado di accedervi.

Quindi ho letto la grande risposta di statenjason e mi ha dato un enorme POV su come rimuovere un DP.
Tuttavia, il mio problema era che da quando ho dichiarato il ItemsSourceProperty membro e il ItemsSource come Private Shadows (private new in C #), non sono stato in grado di caricarlo in fase di progettazione poiché l'utilizzo di MyControlType.ItemsSourceProperty farebbe riferimento al variabile ombreggiata.
Inoltre, quando si utilizzava il ciclo menzionato in enswer sopra (foreach DictionaryEntry ecc.), Mi veniva generata un'eccezione che diceva che la raccolta è cambiata durante l'iterazione.

Pertanto ho escogitato un approccio leggermente diverso in cui DependencyProperty è referenziato in fase di runtime e la raccolta viene copiata nell'array, quindi non viene modificata (VB.NET, scusa):

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: il codice sopra è racchiuso nel costruttore condiviso del controllo personalizzato e non devo verificare se è registrato, perché so che una sottoclasse di Selcetor Fornisce <=> dp.

Si è verificato un problema con un ContentPresenter con diversi datatemplate in cui uno di essi aveva una DependencyProperty con un PropertyChangedCallback Quando si cambia contenuto ContentPresenters in un altro DataTemplate il callback è rimasto.

Nell'evento UserControls Unloaded ho chiamato:

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

Ha funzionato per me

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top