9

単体テストで異常な問題が発生しています。私がテストしているクラスは、実行時に依存関係プロパティを動的に作成し、その依存関係プロパティのタイプは状況によって異なる場合があります。単体テストを作成しているときに、さまざまな型の依存関係プロパティを作成する必要があります。既存の依存関係プロパティを再定義できないため、エラーが発生します。

では、依存関係プロパティの登録を解除するか、既存の依存関係プロパティの型を変更する方法はありますか?

ありがとう!


OverrideMetadata() では、デフォルト値などのごく一部しか変更できないため、役に立ちません。AppDomain アプローチは良いアイデアであり、機能する可能性がありますが、単体テストのために実際に掘り下げたいと思っていたよりも複雑に思えます。

依存関係プロパティの登録を解除する方法が見つからなかったので、この問題を回避するために単体テストをパントして慎重に再編成しました。テスト範囲が少し狭くなっていますが、この問題は実際のアプリケーションでは決して発生せず、単体テスト中にのみ発生するため、問題はありません。

助けてくれてありがとう!

4

6 に答える 6

9

昨日、自分の DependencyProperty 作成クラスをテストしようとしたときに、同様の問題が発生しました。この質問に出くわし、依存関係プロパティを登録解除する実際の解決策がないことに気付きました。そこで、 Red Gate .NET Reflectorを使用して掘り下げて、何が思いつくかを調べました。

オーバーロードを見るとDependencyProperty.Register、それらはすべて を指しているように見えましたDependencyProperty.RegisterCommon。このメソッドには 2 つの部分があります。

まず、物件が既に登録されているかどうかを確認します

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 が依存関係プロパティを登録解除するための正式な方法を持たないことを選択した理由がある可能性が高く、それに逆らおうとする試みは単にトラブルを求めているだけです。

于 2009-09-11T17:13:56.540 に答える
2

他のすべてが失敗した場合は、テストごとに新しい AppDomain を作成できます。

于 2008-09-30T15:52:37.643 に答える
0

次のようにラベルの名前を登録すると:

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

以下を使用して、名前の登録を簡単に解除できます。

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

依存関係プロパティの登録を解除できるとは思いませんが、次のようにメタデータをオーバーライドすることで再定義できます。

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

を継承するカスタム コントロールを作成するというシナリオに直面していましSelectorた。HorizontalItemsSourceVerticalItemsSource

私は ItemsControl プロパティも使用していません。ユーザーがアクセスできるようにしたくありません。

だから私はstatenjasonの素晴らしい答えを読んで、DPを削除する方法について大きなPOVを与えてくれました。
ただし、私の問題は、ItemsSourcePropertyメンバーとItemsSourceas Private Shadows( C# で) を宣言したため、使用するとシャドウ変数が参照されるため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 のサブクラスがそのItemsSourcedp を提供することがわかっているため、登録されているかどうかを確認する必要はありません。

于 2010-11-24T02:24:03.527 に答える