【问题标题】:Pass current object type into base constructor call将当前对象类型传递给基本构造函数调用
【发布时间】:2011-01-18 06:15:51
【问题描述】:

如何获取继承类的Type 并将其传递给同样继承的类的基构造函数?请参阅下面的代码示例:

// VeryBaseClass is in an external assembly
public abstract class VeryBaseClass
{
    public VeryBaseClass(string className, MyObject myObject)
    {

    }
}

// BaseClass and InheritedClass are in my assembly
public abstract class BaseClass : VeryBaseClass
{
    public BaseClass(MyObject myObject) :
        base(this.GetType().Name, myObject) // can't reference "this" (Type expected)
    {

    }
}

public class InheritedClass : BaseClass
{
    public InheritedClass(MyObject myObject)
    {

    }
}

base(typeof(this).Name, myObject) 行不起作用,因为我还不能引用 this,因为对象尚未完成构造,因此不存在。

是否可以抓取当前构造对象的Type

编辑:

将示例更正为orsogufo suggested,但仍然无法正常工作,因为this 未定义。

编辑 2:

澄清一下,我想最终将"InheritedClass" 传递给VeryBaseClass(string className, MyObject myObject) 构造函数。

【问题讨论】:

  • 为什么要这样做?构造函数不应该关心它是被调用来实例化自己的类还是派生类。
  • 我认为你没有抓住重点。定义我使用的框架的基类的外部程序集需要将自身类的字符串表示形式传递给构造函数。到目前为止,我一直按照您的预期(作为字符串)将其放入,但为了理智,我希望它能够在运行时解决。
  • 哦,我明白了,这不是你的课。我仍然认为它在乎是一件坏事:)

标签: c# inheritance constructor types


【解决方案1】:

GetType().Name 不起作用吗?

【讨论】:

  • 不,因为它隐式使用了“this”,您不能使用它来评估构造函数链接参数。
  • 对不起,我的意思不是在构造函数链中,我的意思是 VeryBaseClass 的构造函数可以使用 GetType().Name 而不是期望它被传递。
  • @pdr:假设 VeryBaseClass 总是想要当前类型的名称;虽然在某些情况下这可能是一个合理的设计,但并非总是如此。我将此视为一个更广泛的问题,即“当你想在构造函数链参数中使用 this 时你会做什么”——因为你不能总是将功能推向继承树。
  • @pdr:很遗憾,此时无法修改VeryBaseClass。我只能修改其他两个类:(
  • 好的,我明白你的意思了。我试图解决提问者的问题。我建议如果您想在构造函数链参数中使用“this”,您可能应该认真考虑重新设计。
【解决方案2】:

一个可能的解决方案:

class VeryBase {
    public VeryBase(string name) {
        Console.WriteLine(name);
    }
}

class Base<T> : VeryBase where T : Base<T> {
    protected Base()
        : base(typeof(T).Name) {
    }
}

class Derived : Base<Derived> {
    public Derived() {
    }
}

class Program {
    public static void Main(params string[] args) {
        Derived d = new Derived();
    }
}

【讨论】:

  • 对不起,你是对的。 this.GetType 应该是我的例子。唉,this 未定义。
  • 啊,好吧...好吧,我已经用一个可能的(即使不是很容易使用)解决方案更新了我的帖子...
  • 如果你想添加另一层继承就变得更棘手了,它将使用 Type.Name 的逻辑放入基类而不是派生类。 (在问题中,该逻辑位于三者的中间层。)
  • @Jon:感谢您的评论,我添加了额外的图层。顺便说一句,我认为您的解决方案更好,这只是一个想法:)
  • 好的,现在你如何进一步从 Derived 派生?你也必须使那个通用......这一般是泛型的问题:(
【解决方案3】:

我之前在Google Wave Robot .NET API 中遇到过完全相同的痛苦,我想让构造函数根据属性传递一些值。您可以在derived typebase type 的代码中查看我的解决方案。基本上我将一个委托传递给基本构造函数,然后调用该委托将“this”传递给委托。因此,在您的情况下,您将拥有:

public VeryBaseClass(Func<VeryBaseClass, string> classNameProvider)
{
    this.name = classNameProvider(this);
}

public BaseClass() : base(FindClassName)
{
}

private static string FindClassName(VeryBaseClass @this)
{
    return @this.GetType().Name;
}

真的很丑,但是很管用。

编辑:此方法仅在您可以更改基类构造函数的情况下才有效,如图所示;如果你不能,我不确定它是否真的可行:(

【讨论】:

  • 我以前从未见过@this,似乎让谷歌感到困惑。它是否只允许在没有参数的情况下调用方法,并自动将当前对象放入其中? (我是这么看的)
  • 不,它前面的@只是表示编译器将'this'视为标识符(以将其与关键字区分开来)。检查这个:msdn.microsoft.com/en-us/library/aa664670%28VS.71%29.aspx。您可以用任何参数名称替换“@this”。
  • Argument '1': cannot convert from 'method group' to 'string' 我想我应该在这里提一下,虽然我的示例只显示了基类使用的一个参数,但我的实际实现使用了更多参数。我会更新问题以反映这一点。
  • @CodeSleuth:您是否更改了如图所示的 VeryBaseClass 构造函数?如果是这样,它不应该再期待一个字符串了......
  • 啊,我刚刚看到你不控制基类。在这种情况下,此解决方案对您不起作用 - 我将编辑我的答案以显示这一点。
【解决方案4】:

还有一个选择……

// VeryBaseClass is in an external assembly
public abstract class VeryBaseClass
{
    public VeryBaseClass(string className)
    {
        Console.WriteLine(className);
    }
}

// BaseClass and InheritedClass are in my assembly
public abstract class BaseClass : VeryBaseClass
{
    public BaseClass(string className)
        :
        base(className)
    {

    }
}

public class InheritedClass : BaseClass
{
    public InheritedClass()
        : base(typeof(InheritedClass).Name)
    {
    }
}

【讨论】:

  • 这与将类名输入为字符串相同。我想避免将任何我不需要的东西从InheritedClass 传递给BaseClass 构造函数。
【解决方案5】:

如果在运行时确定继承类的类型比性能更重要,您可以查看 StackTrace 以了解从何处调用构造函数。在此示例中调用 GetInheritedClassName 时,您可能需要编写更多假设代码:

public abstract class BaseClass : VeryBaseClass
{
    private static string GetInheritedClassName
    {
        get
        {
            // Get the current Stack
            StackTrace currentStack = new StackTrace();
            MethodBase method = currentStack.GetFrame(1).GetMethod();

            // 1st frame should be the constructor calling
            if (method.Name != ".ctor")
                return null;

            method = currentStack.GetFrame(2).GetMethod();

            // 2nd frame should be the constructor of the inherited class
            if (method.Name != ".ctor")
                return null;

            // return the type of the inherited class
            return method.ReflectedType.Name;
        }
    }

    public BaseClass(MyObject myObject) :
        base(GetInheritedClassName, myObject)
    {

    }
}

我想不出一种在不影响性能的情况下动态获取继承类名称的好方法。我知道这是您想要避免的,但如果我需要调用具有这些要求的第 3 方程序集,我只需要每个类指定自己的类型:

public abstract class BaseClass : VeryBaseClass
{
    public BaseClass(string className, MyObject myObject) :
        base(className, myObject)
    {

    }
}

public class InheritedClass : BaseClass
{
    public InheritedClass(MyObject myObject) : base("InheritedClass", myObject)
    {

    }
}

【讨论】:

  • 我喜欢这个答案的足智多谋,但你说得对,它是性能密集型的。在查看我的代码之后,我可以看到我不会在代码中的任何时候一次构造许多这些对象,所以这绝对是可行的。绝对是 +1 答案:)
【解决方案6】:

啊哈!我找到了解决方案。您可以使用泛型来做到这一点:

public abstract class VeryBaseClass
{
    public VeryBaseClass(string className, MyObject myObject)
    {
        this.ClassName = className;
    }

    public string ClassName{ get; set; }
}
public abstract class BaseClass<T> : VeryBaseClass
{
    public BaseClass(MyObject myObject)
        : base(typeof(T).Name, myObject)
    {
    }
}

public class InheritedClass : BaseClass<InheritedClass>
{
    public InheritedClass(MyObject myObject) 
        : base(myObject)
    {

    }
}

【讨论】:

  • 天哪,天才!当我完成 TFS 源代码控制布局后,我将对其进行测试:)
  • 非常好,它有效!虽然这意味着我有很多工作要做来改变我现有的课程,但最终还是值得的。谢谢你:)
  • 突然出现了 CRTP(又名 Curiously Recurring Template Pattern)! :)(见en.wikipedia.org/wiki/Curiously_recurring_template_pattern
  • 使用上面的代码,您可以创建一个像这样的类,在其中传入与扩展类不同的内容。 public class InheritedClass : BaseClass { .. }扩展类的类型传入你可能想添加一个 where 子句。公共抽象类 BaseClass : VeryBaseClass where T:BaseClass { ... }
  • 这种技术的问题是不能保证typeof(T) 将始终正确识别正在构建的对象的Type。例如,对于任何进一步继承于InheritedClass 之外的类,此答案都会失败。将所有直接继承者标记为 sealed 或添加 Type-accepting protected 构造函数可能会有所帮助,但无法执行。即使添加@timothy 建议的where 约束仍然很脆弱,因为它取决于每个具体的T 都完全源自BaseClass&lt;T&gt;,出于同样的基本原因,它仍然不一定是T
猜你喜欢
  • 2014-06-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-24
  • 1970-01-01
  • 2023-04-04
相关资源
最近更新 更多