从通用中垂下来,但不失表现力

发布于 2024-10-19 20:38:45 字数 1755 浏览 5 评论 0原文

我有这样的想法:

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向下转换到 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 });
    }
}

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

I've something along this lines:

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

Note: override is not accepted on this case

Due to heavy Reflection usage there are situations where I've to downcast from Something<T> to Something but, I guess because Cases property is hidden, I'm losing Cases data.

How can I circumvent this situation? I've tried to use where T:object but that isn't accepted also.

EDIT:
This is an example of why I need inheritance:

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

But, because 'Cases' is hidden, it will be always null. Without inheritance I would have to write a BIG if-then-else with all the possible generic arguments. And, believe me, I do really have to use someVar is Something and Reflection to construct all this objects. This is a big generic API being converted to other big generic API and so they should not known each other and the conversion should be as transparent as possible.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

恏ㄋ傷疤忘ㄋ疼 2024-10-26 20:38:45

你将无法像这样覆盖它,并且有充分的理由。

想象一下:

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

如果您的覆盖有效,那么将尝试将 Button 引用存储为 Dictionary 中的键。那将是一件坏事。

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

You won't be able to override it like that, and for good reason.

Imagine:

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

If your override worked, that would be trying to store a Button reference as a key in Dictionary<string, Activity>. That would be a Bad Thing.

Perhaps inheritance isn't actually appropriate in this case? If you could explain more about what you're trying to achieve, that would help. Perhaps you don't really need the dictionary as a property? Maybe just a method to fetch by key?

云雾 2024-10-26 20:38:45

这根本行不通,因为 IDictionary接口是不变的。 IDictionary 不能被视为 IDictionary

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

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

对于上述接口,IKeyedCollection可以充当IKeyedCollection; 因为每个 T 都是一个对象

This is flat-out not going to work because the IDictionary<TKey, TValue> interface is invariant. An IDictionary<object, Activity> cannot be treated as an IDictionary<T, Activity>.

What you could do, rather than exposing an entire IDictionary<T, Activity> in your derived class, is simply delegate the calls you want to expose, like this:

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.
}

Alternatively, you could also define your own contravariant interface, something like:

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

For the above interface, an IKeyedCollection<object, Activity> could act as an IKeyedCollection<T, Activity> because every T is an object.

短暂陪伴 2024-10-26 20:38:45

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

这些类型不兼容,因为有些值可以添加到 IDictionary 中,但不能添加到 IDictionary 的每个实例中。想象一下,例如,T 被实例化为 string,并且开发人员通过 Something 在其他地方使用 int 键。这给 Something 实现带来了真正的问题。

我处理此问题的方法是更改​​基本类型 Something ,使其不公开具体类型,而是公开相关的 API。

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

这为 Something 提供了正确子类 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;
    }
  }
}

}

If you attempt to expose incompatible types at the different levels you're going to keep running into problems because at the end of the day you'll end up having to maintain 2 separate objects (or 1 custom object with 2 interfaces it can't completely satisfy).

These types are incompatible because there are values which can be added to IDictionary<object, Activity> which cannot be added to every instantiation of IDictionary<T, Activity>. Imagine for instance T is instatiated as string and the developer uses a int key elsewhere via Something. This creates a real problem for Something<string> implementations.

The way I would approach this is to change the base type Something to not expose a concrete type but instead to expose the relevant APIs.

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

This gives Something<T> the flexbility it needs to properly sub-class Something and be very expressive about the types it wants to expose

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

}

琉璃梦幻 2024-10-26 20:38:45

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

为此,我创建了一个辅助方法来按照 Delegate.CreateDelegate< 的方式创建委托/a>,但允许创建不太通用的委托。必要时会产生沮丧。我在我的博客上详细解释了

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生成此包装器类。它可以在 我的开源库。我还没有写过这个,所以如果有兴趣,你就必须尝试一下(并且可能会看到它失败,因为它仍然很新)。

During heavy reflection usage I also had the need to 'upcast' from generic types. I knew certain calls would be compatible, but I didn't know the types at compile time. If you look at it this way, it is not really 'upcasting' a generic type, but rather, allowing to use generics during reflection by generating the correct downcasts.

To this end I created a helper method to create delegates along the lines of Delegate.CreateDelegate, but allowing to create a less generic delegate. Downcasts are generated where necessary. I explain it in detail on my blog.

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.

A bit later I had a need to create entire less generic wrapper classes for generic classes, so that all method calls would immediately become available as less generic calls during reflection. This might or might not be useful in your scenario as well. For this purpose I created a (not as thoroughly tested) method which allows to generate this wrapper class at runtime using emit. It is available in my open source library. I haven't written about this yet, so when interested you'll just have to try it out (and possibly see it fail since it's still quite new).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文