简体   繁体   English

将通用访问者应用于非通用基类的通用派生类

[英]Apply generic visitor to generic derived class of non-generic base class

Currently I have something like this: 目前我有这样的事情:

public abstract class Base {...}
public class Derived<T> : Base {...}

class Visitor {
    public static void Visit<T>(Derived<T> d) {
        ...
    }
}

My question is, given a Base reference that I know is a Derived instance, how can I apply that Visit function to that object, using the correct generic instantiation? 我的问题是,给定我知道的一个Base引用是一个Derived实例,如何使用正确的泛型实例将Visit函数应用于该对象? I understand that the answer will probably involve a type-checked dynamic downcast, to make sure that the object isn't some other type derived from base, which is all fine. 我知道答案可能会涉及类型检查的动态下降,以确保该对象不是从base派生的其他类型,这很好。 I assume the answer involves reflection, which is also fine, though I'd prefer if there was a way to do it without reflection. 我认为答案涉及反射,这也很好,尽管我更希望有一种不用反射的方法。

It's also ok if the answer involves an abstract method on Base and Derived ; 如果答案涉及BaseDerived的抽象方法也可以; I do have enough control of the classes to add that. 我确实对类有足够的控制权来添加它。 But at the end of the day, I need to call a generic function, correctly instantiated with the T of the Derived type. 但是最终,我需要调用一个通用函数,该函数正确地用Derived类型的T实例化。

Sorry if this is an easy question; 抱歉,这是一个简单的问题。 I come from a C++ background, where my instinct would be to use a CRTP or something else like that, which isn't possible in C#. 我来自C ++背景,我的直觉是使用CRTP或类似的东西,这在C#中是不可能的。

EDIT: 编辑:

Here's an example of what I need to be able to do: 这是我需要做的一个例子:

Base GetSomeDerivedInstance() { ...; return new Derived<...>(); }
var b = GetSomeDerivedInstance();

// This is the line that needs to work, though it doesn't necessarily
// need to have this exact call signature. The only requirement is that
// the instantiated generic is invoked correctly.
Visitor.Visit(b);

(Edited for clarity) (为清楚起见而编辑)

The following will use a variable called "anyvalue" whose type is only known at run-time. 下面将使用一个名为“ anyvalue”的变量,其类型仅在运行时已知。 Then we'll create an instance of your Derived class based on the type of anyvalue. 然后,我们将根据anyvalue的类型创建Derived类的实例。 Once we have that instance, we can use reflection to get the correct Visit method. 一旦有了该实例,就可以使用反射来获取正确的Visit方法。

var anyvalue = 5; // This value could have come from anywhere. 
...    
var derivedType = typeof (Derived<>).MakeGenericType(anyvalue.GetType());
var dvalue = Activator.CreateInstance(derivedType);
var method = typeof(Visitor).GetMethod("Visit");
var genericMethod = method.MakeGenericMethod(new[] { anyvalue.GetType() });
genericMethod.Invoke(null, new [] { dvalue });

What is a little confusing is that this is a skeletal example and you do not use the original value for anything other than getting a run-time type. 有点令人困惑的是,这是一个骨架示例,除了获取运行时类型外,您不会将原始值用于其他任何事情。 In the real world implementation, I would assume a constructor would use that value to set internal state in the Derived instance. 在现实世界的实现中,我假定构造函数将使用该值在Derived实例中设置内部状态。 That is not covered here because that is not part of the question that was asked. 这里不讨论该问题,因为这不是所提出问题的一部分。

UPDATE: 更新:

I think this will do what you want. 我认为这会做您想要的。 Note that I created the itemarray so that we would have some run-time values. 请注意,我创建了itemarray,以便拥有一些运行时值。 They have to be created somewhere. 它们必须在某个地方创建。 So whether they are passed in as object[] or provided some other way, they had to be constructed with a type specifier somewhere. 因此,无论它们是作为object []传入还是以其他方式提供,都必须在某个地方使用类型说明符来构造它们。 Also, this assumes that Derived<> is the only derived class. 同样,这假定Derived <>是唯一的派生类。 Otherwise, this is not safe code. 否则,这不是安全的代码。

var itemarray = new Base[] { new Derived<int>(), new Derived<string>() };

foreach (var baseObject in itemarray)
{
    var derivedType = baseObject.GetType();
    var visitMethod = typeof(Visitor)
        .GetMethod("Visit")
        .MakeGenericMethod(derivedType.GetGenericArguments());
    visitMethod.Invoke(null, new[] { baseObject });
}

The Accept approach does seem a bit more manageable. 接受方法似乎确实更易于管理。 My goal was to answer the question you asked without passing judgment on your approach. 我的目标是在不对您的方法做出判断的情况下回答您提出的问题。 I have needed to use this approach several times. 我需要多次使用这种方法。 I wrote an entity framework about 9 years ago. 大约9年前,我写了一个实体框架。 I had a really hard time doing exactly what you are trying to do. 我确实很难按照自己的意愿去做。 I created base classes that were not generic so that I could share basic functionality regardless of the generic type. 我创建了非通用的基类,因此无论通用类型如何,我都可以共享基本功能。 It proved challenging. 事实证明,这具有挑战性。 I am not sure I would do it the same way now. 我不确定我现在是否会这样做。 I'd probably investigate a few patterns just as you are doing. 正如您正在做的那样,我可能会研究一些模式。

In my opinion, answers involving double-dispatch and More Classes are going to be superior than using reflection to do what inheritance should do for you. 我认为,涉及双调度和更多类的答案将比使用反射来做继承应该为您做的更好。

Normally this means defining an 'accept' method on the visitable class, which simply calls the correct Visit method from the visitor. 通常,这意味着在可访问类上定义一个“ accept”方法,该方法只是从访问者那里调用正确的Visit方法。

class Base
{
    public virtual void Accept(Visitor visitor)
    {
        visitor.Visit(this); // This calls the Base overload.
    }
}

class Derived<T> : Base
{
    public override void Accept(Visitor visitor)
    {
        visitor.Visit(this); // this calls the Derived<T> overload.
    }
}

public class Visitor
{
    public void Visit(Base @base)
    {
        ...
    }

    public void Visit<T>(Derived<T> derived)
    {
        ...
    }
}

Then you can do what you mentioned in your question, with a small modification: 然后,您可以做一个小小的修改即可完成您在问题中提到的内容:

Base b = createDerived();
b.Accept(new Visitor());

If your visit method is a static class that you can't change for whatever reason, you could always wrap this into a dummy instance visitor class which calls the right static method. 如果您的visit方法是一个静态类,无论出于何种原因都无法更改,则可以始终将其包装到一个虚拟实例访问者类中,该类调用正确的static方法。

You should be able to do something like the following: 您应该能够执行以下操作:

Base foo = new Derived<int>();

var method = typeof(Visitor).GetMethod("Visit", BindingFlags.Public | BindingFlags.Static);
method.MakeGenericMethod(foo.GetType().GenericTypeArguments.First()).Invoke(null, new[] {foo});

Perhaps you're meaning something like this? 也许您的意思是这样的?

public class Derived<T>
{
}

public abstract class Derivable<T>
{
    public Derived<T> CreateDerived()
    {
        return new Derived<T>();
    }
}

public class Foo : Derivable<Foo>
{
}

class Visitor
{
    public static void Visit<T>(Derived<T> obj)
    {
        Console.Out.WriteLine("Called!");
    }
}

void Main()
{
    var obj = new Foo();
    var derived = obj.CreateDerived();
    Visitor.Visit(derived);
}

If the creation of the Derived<T> is T -specific, then you'd make the CreateDerived method abstract and implement it for each T . 如果Derived<T>的创建是T特定的,那么您将使CreateDerived方法成为抽象并为每个T实现它。 Or use an IDerivable<T> interface instead if you don't want it as your base class. 如果您不希望将其作为基类,也可以使用IDerivable<T> 接口

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM