我在单元测试中遇到了一个不寻常的问题。我正在测试的类在运行时动态创建依赖项属性,并且该依赖项属性的类型可以根据具体情况而变化。在编写单元测试时,我需要创建具有不同类型的依赖项属性,这会导致错误,因为您无法重新定义现有的依赖项属性。
那么有没有办法取消注册依赖项属性或更改现有依赖项属性的类型?
谢谢!
OverrideMetadata()只允许您更改默认值等少数内容,因此无效。 AppDomain方法是一个好主意,可能会起作用,但似乎比我真正想要深入研究单元测试更复杂。
我从未找到取消注册依赖项属性的方法,因此我进行了细分并仔细重新组织了单元测试以避免出现问题。我的测试覆盖率有所降低,但由于这个问题在真正的应用程序中永远不会发生,并且只有在单元测试期间才能使用它。
感谢您的帮助!
答案 0 :(得分:8)
我在昨天尝试测试自己的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.RegisteredPropertyList
,ItemStructList<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可能有理由选择不采用正式方式取消注册依赖项属性,并试图反对它只是在寻找麻烦。
答案 1 :(得分:2)
如果其他一切都失败了,您可以为每个测试创建一个新的AppDomain。
答案 2 :(得分:0)
我认为您不能取消注册依赖项属性,但您可以通过覆盖这样的元数据来重新定义它:
MyDependencyProperty.OverrideMetadata(typeof(MyNewType),
new PropertyMetadata());
答案 3 :(得分:0)
如果我们为这样的标签注册名称:
Label myLabel = new Label();
this.RegisterName(myLabel.Name, myLabel);
我们可以使用以下方法轻松取消注册名称:
this.UnregisterName(myLabel.Name);
答案 4 :(得分:0)
我遇到的情况是,我创建了一个继承自Selector
的自定义控件,该控件具有两个ItemsSource属性HorizontalItemsSource
和VerticalItemsSource
。
我甚至不使用ItemsControl属性,也不希望用户能够访问它。
所以我读了statenjason's great answer,它给了我一个关于如何移除DP的巨大POV
但是,我的问题是,因为我在{C}中宣布ItemsSourceProperty
成员和ItemsSource
为Private Shadows
(private new
),所以我无法在设计时加载它使用MyControlType.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的子类是否提供ItemsSource
dp。
答案 5 :(得分:0)
ContentPresenter存在一个问题,其中包含不同的Datatemplates,其中一个具有带有PropertyChangedCallback的DependencyProperty 将ContentPresenters内容更改为另一个DataTemplate时,回调仍然存在。
在UserControls Unloaded事件中,我调用了:
BindingOperations.ClearAllBindings(this);
Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Normal, new DispatcherOperationCallback(delegate { return null; }), null);
这对我有用