Приоритеты конструкторов - PullRequest
       4

Приоритеты конструкторов

2 голосов
/ 08 сентября 2011

Я должен построить специальное поведение класса в зависимости от аргумента конструктора. Скажем, рисунок Foo должен стать вечно зеленым, если он был построен (нарисован) зеленым карандашом. Если какой-либо карандаш где-либо используется, Foo должен быть прозрачным ...

Теперь посмотрите на код ниже. Есть ли возможность изменить «выходные данные», чтобы конструктор «видел» реальный тип передаваемого параметра? (на самом деле все они "объект"):

class Program
{
    static void Main(string[] args)
    {
        object[] objs = { new IndexOutOfRangeException(), MyEnum.Beta, 45, new AssemblyName(), new { Name = "a" } };

        for (int i = 0; i < objs.Length; i++)
        {
            Console.WriteLine("{0} => {1} ", i, objs[i]);
        }
        Console.WriteLine("=========================== ");
        for (int i = 0; i < objs.Length; i++)
        {
            Foo myFoo = new Foo(objs[i]);
            Console.WriteLine("{0} => {1}", i, myFoo);
        }
    }
}

public class Foo
{
    object value;
    string typeName;

    public Foo(object obj)
    {
        value = obj;
        typeName = "object";
    }

    public Foo(MyEnum enm)
    {
        value = enm;
        typeName = "MyEnum";
    }

    public Foo(int myInt)
    {
        value = myInt;
        typeName = "int";
    }

    public Foo(Exception ex)
    {
        value = ex;
        typeName = "exception";
    }

    public override string ToString()
    {
        return string.Format("FOO (object = '{0}'; type = '{1}')", value, typeName);
    }
}

public enum MyEnum
{
    Alpha,
    Beta
}

OUTPUT

0 => System.IndexOutOfRangeException: Index was outside the bounds of the array.
1 => Beta
2 => 45
3 =>
4 => { Name = a }

===========================

0 => FOO (object = 'System.IndexOutOfRangeException: Index was outside the bound
s of the array.'; type = 'object')
1 => FOO (object = 'Beta'; type = 'object')
2 => FOO (object = '45'; type = 'object')
3 => FOO (object = ''; type = 'object')
4 => FOO (object = '{ Name = a }'; type = 'object')

EDIT:

Как видно из некоторых ответов, я хотел бы подчеркнуть, что речь идет не о правильной строке, которая должна отображаться в переменной типа, как при использовании value.GetType(), а о "вводе" в правильном конструкторе.

Другими словами, Почему компилятор не определяет правильный тип и «перенаправляет» его в правильный конструктор?

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

Как отмечали ответчики, «путь» к конструктору «строится» во время компиляции, а не во время выполнения. скажи код вроде его

MyEnum en = MyEnum.Beta;
Console.WriteLine("Enum example: obj:{0} Foo:{1}", en, new Foo(en));

Выводит "хороший" вывод:

Enum example: obj:Beta Foo:FOO (object = 'Beta'; type = 'MyEnum')

так что ... по-видимому, любой способ "обойти" это поведение, кроме обнаружения времени выполнения в конструкторе, как предложено Ридом Копси ...?!

Ответы [ 2 ]

3 голосов
/ 08 сентября 2011

Почему компилятор не определяет правильный тип и «перенаправляет» его в правильный конструктор?

Это потому, что вы передаете объект как System.Object.(object[] objs = { n...) Конструктор выбирается во время компиляции , а не во время выполнения.Объявление используемой переменной видно компилятором и используется для проверки на соответствующий тип.

Как вы упомянули в другом комментарии:

хорошо, что если у меня естьбольшой массив объектов, и не знаете, априори их тип?

Именно поэтому компилятор работает именно так.Во время компиляции он не может знать, какой конструктор вам нужен, и, поскольку у вас есть конструктор System.Object, который работает, он выбирается.

Если вы хотите обрабатывать эти конкретные типы отдельно, но по-прежнему создаватьобъект как System.Object, вам нужно будет добавить проверки для этого внутри конструктора для объекта и обрабатывать отдельные случаи отдельно.Однако это не самый обслуживаемый код, если вы это сделаете.

public Foo(object obj)
{
    value = obj;
    typeName = "object";

    // Change typeName if appropriate
    if (obj != null)
    {
        if (obj is MyEnum)
           typeName = "MyEnum";
        else if (obj is int)
           typeName = "int";
        else if (obj is Exception)
           typeName = "exception";
    }
}

Редактировать:

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

// I'd make the object constructor private, to prevent accidental usage:
private Foo(object obj) { ...

public static Foo CreateAppropriateFoo(object obj)
{
     if (obj == null)
         return new Foo(obj); // Use object constructor
     else
     {            
        if (obj is MyEnum)
           return new Foo( (MyEnum)obj );
        else if (obj is int)
           return new Foo( (int)obj );
        else if (obj is Exception)
           return new Foo( (Exception)obj );
     }
}

Это, по крайней мере, предотвращает дублирование логики конструктора, а также делает его немногоболее очевидно, что во время выполнения происходит какая-то логика.

0 голосов
/ 08 сентября 2011

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

var myFoo = new Foo(objs[0] as Exception);

скорее всего будет использовать ctor с параметром Exception.

РЕДАКТИРОВАТЬ: ваш вопрос: Почему компилятор не определяет правильный тип и "перенаправляет" его на правильный конструктор?

мой ответ: потому что вы передаете член массива объектов, поэтому компилятор прав в обработке этого фактического экземпляра объекта как объекта.

это похоже на блоки перехвата, которые должны быть упорядочены от самых общих до более общих понижения, у вас есть общий перехватчик вверху, где ваш ctor принимает объект, и вы не должны удивляться, что передача значения из массива объектов Компилятор использует наиболее подходящий кандидат. Вот почему я предложил вам выполнить приведение к вызову ctor, чтобы вы могли сказать, что хотите привести objs [0] к объекту Exception, или компилятор будет рассматривать его как объект.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...