Назначить делегата на основе типа? - PullRequest
1 голос
/ 07 августа 2009

У меня был быстрый вопрос. Что-то действительно не так с этим кодом. Я хочу использовать дженерики и делегаты, а также, возможно, дженерики, если это применимо. Я работаю с некоторыми кодами, сгенерированными API, и сгенерированные объекты очень похожи. Я вижу, что все они реализуют интерфейс, поэтому я попытался создать один класс с несколькими методами для обработки различных сценариев. Вот пример кода. Это просто чувствует себя неправильно на многих уровнях. Пожалуйста, скажите мне, как я могу сделать этот код лучше. Небольшой совет по рефакторингу, если хотите. И непременно разорвите его в клочья. Я хочу лучше писать код и учиться делать все правильно.

private delegate IsomeEntity DisplayDelegate(IsomeEntity display);

public IsomeEntity Display<T>()
{
    DisplayDelegate _del = null;
    IsomeEntity display = factory.CreateObject(typeof(T).Name);

    if (display.GetType() == typeof(ADisplayEntity))
        _del = ADisplayEntity;

    if (display.GetType() == typeof(BDisplayEntity))
        _del = BDisplayEntity;

    if (display.GetType() == typeof(CDisplayEntity))
        _del = CDisplayEntity;


    return _del(display);
}

public ADisplayEntity ADisplayEntity(IsomeEntity display)
{
    ADisplayEntity ade = display as ADisplayEntity;

    try
    {
        ADisplay o = new ADisplay();
        ADisplayEntity response = o.ADisplay(ade);
        return response;
    }
    catch (Exception ex)
    {
        Exception newEx;
        if (someExceptionHandler.HandleException(ex, this, out newEx))
            throw newEx;
    }

    return null;
}

public BDisplayEntity BDisplayEntity(IsomeEntity display)
{
    BDisplayEntity dde = display as BDisplayEntity;

    try
    {
        BDisplay o = new BDisplay();
        BDisplayEntity response = o.BDisplay(bde);
        return response;
    }
    catch (Exception ex)
    {
        Exception newEx;
        if (someExceptionHandler.HandleException(ex, this, out newEx))
            throw newEx;
    }

    return null;
}

Ответы [ 5 ]

0 голосов
/ 07 августа 2009

Я собираюсь предположить, так как вы упомянули, что материал был автоматически сгенерирован, что изменение ISomeEntity находится вне окна (в противном случае я бы предложил добавить метод Display () в ISomeEntity, а затем просто вызывать его непосредственно на объекте через интерфейс. Каждый объект будет реализовывать свою собственную версию Display ()).

Итак, если я понимаю, что ваш код пытается делать правильно (это не совсем понятно), я бы предложил создать интерфейс IDisplay, наследуя от него ADisplay и BDisplay. Этот интерфейс будет иметь метод Display (), который принимает ISomeEntity и возвращает ISomeEntity. Однако, если ADisplay.Display (ISomeEntity) получает BDisplayEntity, вы бы выбросили исключение.

Затем я бы создал IDictionary, который вы бы сохранили в виде поля в классе, который имеет этот основной метод Display (я собираюсь назвать его Displayer). Этот словарь будет хранить то, что IDisplay использовать для каждого типа (то есть typeof (ADisplayEntity) -> new ADisplay ()).

Затем вы можете добавить свой основной метод Display в Displayer, но теперь сделайте так, чтобы он возвращал универсальный T, поскольку T - это тип, который вы создаете и возвращаете. Этот метод ищет, какой IDisplay ему нужен, и использует его на фабрике, созданной ISomeEntity, и результат которой возвращается.

Использование словаря означает, что вы не получите дрянную партию операторов if и что вы можете легко добавить больше IDisplays, просто добавив в словарь.

Вот мой код, который компилируется в VS2008.

public interface ISomeEntity
{

}

public class EntityFactory
{
    public ISomeEntity CreateObject(string name)
    {
        //Do factory stuff here
        return null;
    }
}

public class ADisplayEntity : ISomeEntity
{
}



public class BDisplayEntity : ISomeEntity
{
}

public interface IDisplay
{
    ISomeEntity Display(ISomeEntity entity);
}

public class ADisplay : IDisplay
{
    public ISomeEntity Display(ISomeEntity entity)
    {
        ADisplayEntity aEntity = entity as ADisplayEntity;
        if (aEntity == null)
            throw new ArgumentException("Wrong type");

        //Do whatever happens when you convert parameter entity into a
        //"response" ADisplayEntity. I'm just returning a new 
        //ADisplayEntity to make it compile for me
        return new ADisplayEntity();
    }
}

public class BDisplay : IDisplay
{
    public ISomeEntity Display(ISomeEntity entity)
    {
        BDisplayEntity bEntity = entity as BDisplayEntity;
        if (bEntity == null)
            throw new ArgumentException("Wrong type");

        //Do whatever happens when you convert parameter entity into a
        //"response" BDisplayEntity. I'm just returning a new 
        //BDisplayEntity to make it compile for me
        return new BDisplayEntity();
    }
}



public class Displayer
{
    private IDictionary<Type, IDisplay> displayers;
    private EntityFactory factory;


    public Displayer()
    {
        factory = new EntityFactory();
        displayers = new Dictionary<Type, IDisplay>
                        {
                            { typeof(ADisplayEntity), new ADisplay() },
                            { typeof(BDisplayEntity), new BDisplay() }
                        };
    }


    public T Display<T>() where T : class, ISomeEntity
    {
        T entity = factory.CreateObject((typeof(T).Name)) as T; //Type-safe because of the factory
        IDisplay displayer = displayers[typeof(T)];
        return displayer.Display(entity) as T; //Typesafe thanks to each IDisplay returning the correct type
    }
}
0 голосов
/ 07 августа 2009

Если вы хотите обобщить эти методы, вы можете сделать что-то вроде этого:

private delegate IsomeEntity DisplayDelegate<T>(IsomeEntity display);

    public IsomeEntity DisplayMethod<T>() where T : IsomeEntity
    {
        DisplayDelegate<T> _del = new DisplayDelegate<T>(DoDisplay<T>);
        IsomeEntity entity = factory.CreateObject(typeof(T).Name);

        return _del(entity);
    }   

    public IsomeEntity DoDisplay<T>(IsomeEntity entity)
    {
        try
        {
            Display<T> o = new Display<T>();
            Entity<T> response = o.Display(entity);
            return response;
        }
        catch (Exception ex)
        {
            if (someExceptionHandler.HandleException(ex, this, out newEx))
                throw newEx;
        }
    }

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

    public IsomeEntity DisplayMethod<T>() where T : IsomeEntity
    {            
        IsomeEntity entity = factory.CreateObject(typeof(T).Name);

        return DoDisplay<T>(entity);
    }   

    public IsomeEntity DoDisplay<T>(IsomeEntity entity)
    {
        try
        {
            Display<T> o = new Display<T>();
            Entity<T> response = o.Display(entity);
            return response;
        }
        catch (Exception ex)
        {
            if (someExceptionHandler.HandleException(ex, this, out newEx))
                throw newEx;
        }
    }
0 голосов
/ 07 августа 2009

Хорошо, если я буду правильно следовать за вами, ADisplayEntity и BDisplayEntity - оба сгенерированных класса, которые выглядят почти одинаково, верно? И вы пытаетесь создать какой-то универсальный метод фабрики делегатов (тот, который вы назвали Display<T>), который позволяет вызывающей стороне указывать тип отображаемой сущности, которую они хотят, приводить как единый созданный вами интерфейс, который реализуют сущности. , да? Я полагаю, что классы ADisplay и BDisplay не реализуют общий метод Display(), поэтому у вас есть вызовы ADisplay и BDisplay. Хм ... это то же имя, что и класс. Опечатка? То, что вы перечислили, даже не скомпилируется.

Я думаю, нужен лучший пример. Я понимаю, что вы пытаетесь санировать свой код, но, возможно, какой-то реальный код с изменениями имени мог бы быть лучше. Я хотел бы видеть, что на самом деле ADisplay, BDisplay, ADisplayEntity и BDisplayEntity.

Тем не менее, если различные классы A/BDisplay и A/BDisplayEntity действительно настолько различны, что вы не можете объединить эти методы делегатов в один метод, то вы в значительной степени делаете единственное, что вы можете сделать, чтобы достичь своего исходного Цель.

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

0 голосов
/ 07 августа 2009

почему не что-то вроде простого? зачем вам делегаты .. и т.д.

 public static ISomeEntity DisplayEntity(ISomeEntity display)
{

         ISomeEntity result;
           if (entity is ADisplayEntity)
            {
                ADisplay disp = new ADisplay();
                result = disp.ADisplayFunc();
            }
          if(entity is BDisplayEntity)
           {
                BDisplay disp = new BDisplay();
                result = disp.BDisplayFunc();
           }

    return result;
}

Конечно, было бы больше смысла, если бы вы могли ADisplay и BDisplay также следовать интерфейсу, например IDisplay , тогда вам просто нужно вернуться 1010 * IDisplay.Display (ISomeEntity)

Чтобы следовать этому, вы можете обернуть ваши дисплеи, как это ..

public interface IDisplay
{
    public ISomeEntity Display(ISomeEntity entity);
}

public class AWrappedDisplay: IDisplay
{
    public ISomeEntity Display(ISomeEntity entity)
    {
        ADisplay disp = new ADisplay();
        return disp.ADisplayFunc(entity);
    }

}

public class BWrappedDisplay : IDisplay
{
    public ISomeEntity Display(ISomeEntity entity)
    {
        BDisplay disp = new BDisplay();
        return disp.BDisplayFunc(entity);
    }

}

public static IDisplay Factory(Type t)
        {
           IDisplay disp = null;
           if (t == typeof(ADisplayEntity))
               disp = new AWrappedDisplay();

           if (t == typeof(BDisplayEntity))
               disp = new BWrappedDisplay();

            return disp;
        }

и тогда вы можете вызвать свой DisplayEntity следующим образом

  public static ISomeEntity DisplayEntity(ISomeEntity display)
    {
        IDisplay disp = Factory(display.GetType());
        ISomeEntity newDisplayEntity = disp.Display(display);

        return newDisplayEntity;
    }
0 голосов
/ 07 августа 2009

Вы можете передавать делегаты и лямбда-функции как общие значения, так что-то вроде этого:

public ISomeEntity Display<T>( Func<T, ISomeEntity> conversion )
{
    IsomeEntity display = factory.CreateObject(typeof(T).Name);
    return conversion(display);
}

Тогда вызов будет:

var myNewEntity = Display( 
    x => ADisplay.GetADisplay(x as ADisplayEntity) );

и т. Д.

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

Вы даже можете хранить их в словарях и искать их.

...