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

时间:2016-07-26 01:15:19

标签: c# generics inheritance visitor

目前我有这样的事情:

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

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

我的问题是,如果我知道的Base引用是Derived实例,那么如何使用正确的通用实例将Visit函数应用于该对象?我理解答案可能涉及一个经过类型检查的动态向下转换,以确保该对象不是从base派生的其他类型,这一切都很好。我假设答案涉及反思,这也很好,但我更喜欢有没有反思的方法。

如果答案涉及BaseDerived上的抽象方法,那也没关系;我确实有足够的控制权来添加它。但是在一天结束时,我需要调用一个泛型函数,使用派生类型的T正确实例化。

对不起,如果这是一个简单的问题;我来自C ++背景,我的直觉是使用CRTP或类似的东西,这在C#中是不可能的。

修改

以下是我需要做的一个例子:

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

4 个答案:

答案 0 :(得分:1)

(为清晰起见编辑)

以下将使用名为“anyvalue”的变量,其类型仅在运行时已知。然后我们将根据anyvalue的类型创建Derived类的实例。一旦我们有了这个实例,我们就可以使用反射来获得正确的访问方法。

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

有点令人困惑的是,这是一个骨架示例,除了获取运行时类型之外,您不使用原始值。在现实世界的实现中,我假设构造函数将使用该值来设置Derived实例中的内部状态。这里没有涉及,因为这不是被问到的问题的一部分。

<强>更新

我认为这会做你想要的。请注意,我创建了itemarray,以便我们有一些运行时值。他们必须在某个地方创建。因此,无论它们是作为object []传递还是以其他方式提供,它们都必须在某处构造一个类型说明符。此外,这假定Derived&lt;&gt;是唯一的派生类。否则,这不是安全的代码。

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

Accept方法似乎更容易管理。我的目标是回答你问的问题而不判断你的方法。我需要多次使用这种方法。我在9年前写了一个实体框架。我真的很难完成你想要做的事情。我创建了非通用的基类,因此无论泛型类型如何,我都可以共享基本功能。事实证明具有挑我不确定我现在会以同样的方式做到这一点。我可能会像你一样调查一些模式。

答案 1 :(得分:1)

在我看来,涉及双重发送和更多类的答案将优于使用反射进行继承应该为您做的事情。

通常这意味着定义一个&#39;接受&#39;可访问类的方法,只需从访问者调用正确的访问方法。

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

然后,您可以通过一些小修改来完成您在问题中提到的内容:

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

如果你的访问方法是一个静态类,你不能因为某种原因而改变,你总是可以把它包装成一个调用正确静态方法的虚拟实例访问者类。

答案 2 :(得分:0)

您应该可以执行以下操作:

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

答案 3 :(得分:0)

也许你意味着这样的事情?

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

如果Derived<T>的创建是T特定的,那么您将CreateDerived方法摘要并为每个T实施。或者,如果您不希望将其作为基类,请使用IDerivable<T> 界面