9

我在单元测试中遇到了一个不寻常的问题。我正在测试的类在运行时动态创建一个依赖属性,并且该依赖属性的类型可能会因环境而异。在编写单元测试时,我需要创建具有不同类型的依赖属性,这会导致错误,因为您无法重新定义现有的依赖属性。

那么有没有办法取消注册依赖属性或更改现有依赖属性的类型?

谢谢!


OverrideMetadata() 只允许您更改很少的内容,例如默认值,因此它没有帮助。AppDomain 方法是一个好主意,并且可能有效,但似乎比我真正想为了单元测试而深入研究的要复杂。

我从来没有找到取消注册依赖属性的方法,所以我小心翼翼地重新组织了我的单元测试以避免这个问题。我得到的测试覆盖率有所降低,但由于这个问题永远不会在实际应用程序中发生,而且只有在单元测试期间我才能忍受它。

谢谢您的帮助!

4

6 回答 6

9

我昨天在尝试测试我自己的 DependencyProperty 创建类时遇到了类似的问题。我遇到了这个问题,并注意到没有真正的解决方案来取消注册依赖属性。所以我使用Red Gate .NET Reflector进行了一些挖掘,看看我能想出什么。

看着DependencyProperty.Register过载,他们似乎都指向DependencyProperty.RegisterCommon。该方法有两个部分:

首先检查房产是否已经注册

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

二、注册DependencyProperty

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

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

两个部分都DependencyProperty.PropertyFromName以 HashTable 为中心。我还注意到 , DependencyProperty.RegisteredPropertyListItemStructList<DependencyProperty>但没有看到它在哪里使用。但是,为了安全起见,我想如果可能的话,我也会尝试从中删除。

所以我结束了下面的代码,它允许我“取消注册”一个依赖属性。

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

它工作得很好,我可以运行我的测试而不会出现“AlreadyRegistered”异常。但是,我强烈建议您不要在任何类型的生产代码中使用它。MSFT 选择不使用正式的方式来取消注册依赖项属性可能是有原因的,并且试图违背它只是自找麻烦。

于 2009-09-11T17:13:56.540 回答
2

如果一切都失败了,您可以为每个测试创建一个新的 AppDomain。

于 2008-09-30T15:52:37.643 回答
0

我不认为您可以取消注册依赖属性,但您可以通过覆盖元数据来重新定义它,如下所示:

MyDependencyProperty.OverrideMetadata(typeof(MyNewType), 
                     new PropertyMetadata());
于 2008-09-28T21:52:43.770 回答
0

如果我们为这样的标签注册名称:

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

我们可以使用以下方法轻松注销名称:

this.UnregisterName(myLabel.Name);
于 2009-06-14T19:30:56.033 回答
0

我面临的情况是,我创建了一个继承自该控件的自定义控件,该控件Selector具有两个 ItemsSource 属性,HorizontalItemsSource并且VerticalItemsSource.

我什至不使用 ItemsControl 属性,也不希望用户能够访问它。

所以我读了 statenjason 的好答案,它给了我一个关于如何删除 DP 的巨大观点。
但是,我的问题是,由于我声明了ItemsSourceProperty成员和ItemsSourceas Private Shadowsprivate new在 C# 中),我无法在设计时加载它,因为 usingMyControlType.ItemsSourceProperty将引用阴影变量。
此外,当使用上面提到的循环(foreach DictionaryEntry等)时,我抛出了一个异常,说集合在迭代期间发生了变化。

因此,我想出了一个稍微不同的方法,其中 DependencyProperty 在运行时被硬编码,并且集合被复制到数组中,所以它不会改变(VB.NET,抱歉):

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)

重要说明:上面的代码都包含在自定义控件的共享构造函数中,我不必检查它是否已注册,因为我知道 Selcetor 的子类确实提供了该ItemsSourcedp。

于 2010-11-24T02:24:03.527 回答
0

具有不同数据模板的 ContentPresenter 存在问题,其中一个具有带有 PropertyChangedCallback 的 DependencyProperty 将 ContentPresenters 内容更改为另一个 DataTemplate 时,回调仍然存在。

在 UserControls Unloaded 事件中,我调用了:

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

这对我有用

于 2018-02-09T15:10:26.137 回答