C #: типы приведения динамически - PullRequest
6 голосов
/ 19 февраля 2009

У меня сейчас такой код:

private void FillObject(Object MainObject, Foo Arg1, Bar Arg2)
{
    if (MainObject is SomeClassType1)
    {
        SomeClassType1 HelpObject = (SomeClassType1)MainObject;
        HelpObject.Property1 = Arg1;
        HelpObject.Property2 = Arg2;
    }
    else if (MainObject is SomeClassType2)
    {
        SomeClassType2 HelpObject = (SomeClassType2)MainObject;
        HelpObject.Property1 = Arg1;
        HelpObject.Property2 = Arg2;
    }
}

Предполагая, что SomeClassType1 и SomeClassType2 имеют одинаковый набор свойств, которые я хочу назначить (хотя они могут отличаться в других), возможно ли динамически привести MainObject к соответствующему типу и затем присвоить значение, не дублируя код ? Вот что я хотел бы увидеть в конце:

private void FillObject(Object MainObject, Foo Arg1, Bar Arg2)
{
    Type DynamicType = null;

    if (MainObject is SomeClassType1)
    {
        DynamicType = typeof(SomeClassType1);
    }
    else if (MainObject is SomeClassType2)
    {
        DynamicType = typeof(SomeClassType2);
    }

    DynamicType HelpObject = (DynamicType)MainObject;
    HelpObject.Property1 = Arg1;
    HelpObject.Property2 = Arg2;
}

И, очевидно, C # жалуется на невозможность найти DynamicType:

Не удалось найти тип или имя пространства имен DynamicType (отсутствует директива using или ссылка на сборку?)

Возможно ли что-то подобное в C # 2.0? Если это более грязно, чем мой текущий код, тогда я не вижу смысла в этом, но мне очень интересно это выяснить. Спасибо!

РЕДАКТИРОВАТЬ: Просто чтобы уточнить, я прекрасно понимаю, что реализация интерфейса является наиболее подходящим и, вероятно, правильным решением. Тем не менее, мне больше интересно посмотреть, как я могу сделать это без реализации интерфейса. Спасибо за отличные ответы!

Ответы [ 7 ]

15 голосов
/ 19 февраля 2009

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

public interface IMyInterface
{
   public Foo Property1 {get; set;}
   public Bar Property2 {get;set;}
}

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

private void FillObject<T>(T MainObject, Foo Arg1, Bar Arg2) 
    where T : IMyInterface
{
    MainObject.Property1 = Arg1;
    MainObject.Property2 = Arg2;
}

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

9 голосов
/ 19 февраля 2009

По сути, вы здесь пишете оператор switch на основе типа объекта. Нет хорошего способа сделать это, кроме вашего первого примера (который в лучшем случае утомителен).

Я написал небольшую структуру для переключения типов, которая делает синтаксис более лаконичным. Это позволяет вам писать код, подобный следующему.

TypeSwitch.Do(
    sender,
    TypeSwitch.Case<Button>(
        () => textBox1.Text = "Hit a Button"),
    TypeSwitch.Case<CheckBox>(
        x => textBox1.Text = "Checkbox is " + x.Checked),
    TypeSwitch.Default(
        () => textBox1.Text = "Not sure what is hovered over"));

Сообщение в блоге: http://blogs.msdn.com/jaredpar/archive/2008/05/16/switching-on-types.aspx

7 голосов
/ 19 февраля 2009

Сможете ли вы внести изменения в SomeClassType1, SomeClassType2 и т. Д.? Если да, то я предложу вам создать интерфейс, содержащий ваши общие свойства, а затем ввести тип этого интерфейса в FillObject() для установки свойств.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SO566510
{
    interface IMyProperties
    {
        int Property1 { get; set; }
        int Property2 { get; set; }
    }

    class SomeClassType1 : IMyProperties
    {
        public int Property1 { get; set; }
        public int Property2 { get; set; }
    }

    class SomeClassType2 : IMyProperties
    {
        public int Property1 { get; set; }
        public int Property2 { get; set; }
    }
    class Program
    {
        static void Main(string[] args)
        {
            var obj1 = new SomeClassType1();
            var obj2 = new SomeClassType2();

            FillObject(obj1, 10, 20);
            FillObject(obj2, 30, 40);

        }

        private static void FillObject(IMyProperties objWithMyProperties
                                   , int arg1, int arg2)
        {
            objWithMyProperties.Property1 = arg1;
            objWithMyProperties.Property2 = arg2;
        }
    }
}
4 голосов
/ 19 февраля 2009
private void FillObject(Object MainObject, Foo Arg1, Bar Arg2)
{
    Type t = MainObject.GetType();

    t.GetProperty("Property1").SetValue(MainObject, Arg1, null);
    t.GetProperty("Property2").SetValue(MainObject, Arg2, null);
}
3 голосов
/ 19 февраля 2009

Некоторые идеи:

  1. Оба типа наследуются от одного типа с общими свойствами
  2. Оба типа реализуют один и тот же интерфейс с общими свойствами
  3. Используйте отражение, чтобы установить свойства, а не устанавливать их напрямую (это, вероятно, более уродливо, чем стоит)
  4. Используйте новую динамическую функцию , я не пробовал этого, но похоже, что она может решить вашу проблему
2 голосов
/ 19 февраля 2009

Вместо попытки приведения, возможно, вы могли бы попытаться установить свойства с помощью отражения.

0 голосов
/ 01 апреля 2010

Я динамически преобразую объекты, используя Reflection в приложении ASP.NET MVC. По сути, я перечисляю свойства класса, нахожу соответствующее значение в хранилище данных, динамически преобразую его и назначаю его экземпляру объекта. Смотрите мой блог Динамическое приведение с .NET

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