Вопрос по constructor, inheritance, types, c# – Передать текущий тип объекта в вызов базового конструктора

9

Как мне взять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, но все еще не работает, так какthis не определено

РЕДАКТИРОВАТЬ 2:

Просто чтобы уточнить, я хочу в конечном итоге"InheritedClass" передаваться вVeryBaseClass(string className, MyObject myObject) конструктор.

О, я вижу, это не твой класс. Я все еще думаю, что это плохо, что это волнует :) AakashM
Я думаю, что вы упускаете суть. Внешняя сборка, которая определяет базовый класс фреймворка, который я использую, требовала, чтобы строковое представление самого класса передавалось в конструктор. До сих пор я вставлял его так, как вы ожидаете (как строку), но я бы предпочел, чтобы он работал во время выполнения ради здравомыслия. Codesleuth
почему ты хочешь сделать это? Конструктор не должен заботиться о том, вызывается ли он для создания своего собственного класса или производного класса. AakashM

Ваш Ответ

5   ответов
3

Возможное решение:

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();
    }
}
Хорошо, теперь, как вы дальше производите от Derived? Тебе тоже нужно сделать это дженерик ... Это проблема с дженериками вообще :( Jon Skeet
Извини, ты прав. this.GetType должен был быть моим примером. Увы,this не определено Codesleuth
Ах, хорошо ... я обновил свой пост с возможным (даже не очень простым) решением ... Paolo Tedesco
@Jon: спасибо за замечание, я добавил дополнительный слой. Кстати, я думаю, что ваше решение лучше, это была просто идея :) Paolo Tedesco
Это становится сложнее, если вы хотите добавить еще один уровень наследования, и это помещает логику использования Type.Name в базовый класс вместо производного класса. (В вопросе, эта логика находится на среднем уровне трех.) Jon Skeet
18

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

    }
}
Отлично, это работает! Хотя это означает, что у меня есть много работы, чтобы изменить существующие классы, но в конечном итоге это будет стоить того. Спасибо :) Codesleuth
Billiant! Используя это 9 лет спустя ... Mike Devenney
Боже мой, гений! Я протестирую это, когда закончу разделывать свой макет TFS Source Control :) Codesleuth
Внезапно появляется CRTP (он же CURLYURURRING Template Pattern)! :) (увидетьen.wikipedia.org/wiki/Curiously_recurring_template_pattern) Macke
с помощью приведенного выше кода вы можете создать класс, подобный этому, в котором вы передадите что-то отличное от расширенного класса. Открытый класс InheritedClass: BaseClass <int> {..}, чтобы обеспечить передачу только вам типа расширенного класса. Возможно, вы захотите добавить предложение where. открытый абстрактный класс BaseClass <T>: VeryBaseClass, где T: BaseClass <T> {...} timothy
0

Еще один вариант ...

// 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)
    {
    }
}
Это было бы то же самое, что вводить имя класса в виде строки. Я хотел избежать передачи всего, что мне не нужно, вBaseClass конструктор изInheritedClass. Codesleuth
6

У меня была точно такая же боль до сих пор вGoogle Wave Robot .NET API где я хотел, чтобы конструктор передавал некоторые значения, основанные на атрибутах. Вы можете взглянуть на мое решение в коде дляпроизводный тип ибазовый тип, По сути, я передаю делегат базовому конструктору, а затем вызываю этот делегат, передавая делегату слово «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;
}

Это действительно некрасиво, но это работает.

РЕДАКТИРОВАТЬ: Этот подход работает, только если вы можете изменить конструктор базового класса, как показано; если вы не можете, я не уверен, что это вообще возможно :(

@ Downvoter: Хотите прокомментировать? Jon Skeet
Ой, ты был так близко! Я, конечно, не могу быть первым человеком, у которого была эта проблема, должно быть решение (или объяснение, почему нет) где-то. Codesleuth
@CodeSleuth: вы изменили конструктор VeryBaseClass, как показано? Если так, то больше не следует ожидать строку ... Jon Skeet
Нет, перед ним просто говорится, что компилятор должен рассматривать «это» как идентификатор (чтобы отличить его от ключевого слова). Проверь это:msdn.microsoft.com/en-us/library/aa664670%28VS.71%29.aspx, Вы можете заменить '@this' на любое имя параметра. Paolo Tedesco
3

ения более важна, чем производительность, вы можете взглянуть на 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)
    {

    }
}

Я не могу придумать отличный способ динамически получить имя унаследованного класса без последствий для производительности. Я понимаю, что вы хотели избежать, но если бы мне нужно было вызывать стороннюю сборку с этими требованиями, я бы просто потребовал, чтобы каждый класс указывал свой собственный тип:

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 ответ :) Codesleuth

Похожие вопросы