1

我有这样的事情:

public class Something
{
    private IDictionary<object,Activity> fCases;

    public IDictionary<object,Activity> Cases
    {
        get { return fCases; }
        set { fCases = value; }
    }
}

public sealed class Something<T> : Something 
{
    private IDictionary<T,Activity> fCases;

    public override IDictionary<T,Activity> Cases
    {
        get { return fCases; }
        set { fCases = value; }
    }
}

注意:在这种情况下不接受覆盖

由于大量使用反射,在某些情况下我不得不沮丧Something<T>Something但我想因为Cases属性是隐藏的,所以我正在丢失Cases数据。

我该如何规避这种情况?我试过使用where T:object,但这也不被接受。

编辑:这是为什么我需要继承的一个例子:

if (someVar is Something) {

    if (someVar.GetType().IsGenericType) 
    {
        // Construct AnotherObject<T> depending on the Something<T>'s generic argument
        Type typeArg = someVar.GetType().GetGenericArguments()[0],
            genericDefinition = typeof(AnotherObject<>),
            typeToConstruct = genericDefinition.makeGenericType(typeArgs);

        object newAnotherObject = Activator.CreateInstance(typeToConstruct);

        // Pass Something 'Cases' property to AnotherObject<T>
        constructedType.InvokeMember(
            "Cases",
            BindingFlags.Instance | BindingFlags.Public | BindingFlags.SetProperty,
            null,
            newActivity,
            new Object[] { someVar.Cases });
    }
}

但是,因为“案例”是隐藏的,所以它将始终为空。如果没有继承,我将不得不用所有可能的通用参数编写一个 BIG if-then-else。而且,相信我,我确实必须使用someVar is SomethingReflection来构造所有这些对象。这是一个大型通用 API 正在转换为其他大型通用 API,因此它们不应该相互认识,并且转换应该尽可能透明。

4

4 回答 4

4

您将无法像那样覆盖它,这是有充分理由的。

想象:

Something x = new Something<string>();
Button key = new Button();
x.Cases[key] = new Activity();

如果您的覆盖有效,那将尝试将Button引用存储为Dictionary<string, Activity>. 那将是一件坏事。

在这种情况下,也许继承实际上并不合适?如果你能解释更多关于你想要达到的目标,那会有所帮助。也许您真的不需要字典作为属性?也许只是一种按键获取的方法?

于 2011-03-02T19:17:59.983 回答
2

这是完全行不通的,因为IDictionary<TKey, TValue>接口是不变的。AnIDictionary<object, Activity>不能被视为IDictionary<T, Activity>

可以做的不是IDictionary<T, Activity>在派生类中公开一个整体,而是简单地委托您想要公开的调用,如下所示:

public class Something
{
    protected IDictionary<object, Activity> Cases { get; set; }
}

public sealed class Something<T> : Something 
{
    public Activity GetCase(T key)
    {
        return Cases[key];
    }

    public void AddCase(T key, Activity case)
    {
        Cases.Add(key, case);
    }

    // etc. etc.
}

或者,您也可以定义自己的逆变接口,例如:

interface IKeyedCollection<in TKey, TValue>
{
    TValue this[TKey key] { get; set; }
    void Add(TKey key, TValue value);
}

对于上面的接口, anIKeyedCollection<object, Activity> 可以充当 anIKeyedCollection<T, Activity>因为 everyT是 an object

于 2011-03-02T19:23:13.993 回答
1

如果您尝试在不同级别公开不兼容的类型,您将继续遇到问题,因为最终您将不得不维护 2 个单独的对象(或 1 个具有 2 个接口的自定义对象,它不能完全满足)。

这些类型是不兼容的,因为有些值可以添加,IDictionary<object, Activity>但不能添加到IDictionary<T, Activity>. 想象一下,例如Tstring开发人员在int其他地方通过Something. 这给实现带来了真正的问题Something<string>

我解决这个问题的方法是将基本类型更改Something为不公开具体类型,而是公开相关的 API。

public abstract class Something {
  public abstract IEnumerable<KeyValuePair> GetElements(); 
  public abstract bool TryGetValue(object key, out Activity value);
}

这提供Something<T>了适当的子类化所需的灵活性,Something并且对它想要公开的类型非常有表现力

public sealed class Something<T> : Something {
  private IDictionary<T,Activity> fCases;

  public override IDictionary<T,Activity> Cases
  {
    get { return fCases; }
    set { fCases = value; }
  }

  public override IEnumerable<KeyValuPair<object, Activity>> GetElements() {
    foreach (var cur in fCases) {
      yield return new KeyValuePair<object, Activity>(cur.Key, cur.Value);
    }
  }

  public override bool TryGetValue(object key, out Activity activity) {
    try {
      T typedKey = (T)key;
      return fCases.TryGetValue(typedKey, out activity);
    } catch (InvalidCastException) {
      activity = null;
      return false;
    }
  }
}

}

于 2011-03-02T19:15:23.717 回答
1

在大量反射使用期间,我还需要从泛型类型“向上转换”。我知道某些调用是兼容的,但在编译时我不知道类型。如果您这样看,它并不是真正“向上转换”泛型类型,而是通过生成正确的向下转换来允许在反射期间使用泛型

为此,我创建了一个帮助方法来创建沿 Delegate.CreateDelegate 行的委托,但允许创建一个不太通用的委托。必要时会生成向下转换。我在我的博客上详细解释了它。

MethodInfo methodToCall = typeof( string ).GetMethod( "Compare" );
Func<object, object, int> unknownArgument
    = DelegateHelper.CreateDowncastingDelegate<Func<object, object, int>>(
          null, methodToCall );
unknownArgument( "test", "test" ); // Will return 0.
unknownArgument( "test", 1 ); // Will compile, but throw InvalidCastException.

稍后,我需要为泛型类创建完整的非泛型包装类,以便在反射期间所有方法调用都可以立即作为非泛型调用可用。这在您的场景中可能有用也可能没用。为此,我创建了一个(没有经过彻底测试)方法,它允许在运行时使用 emit 生成这个包装类。它在我的开源库中可用。我还没有写过这个,所以当你感兴趣的时候,你只需要尝试一下(并且可能会看到它失败,因为它仍然很新)。

于 2011-05-30T17:49:12.830 回答