.Net 4.0优化代码,用于重构现有的“if”条件和“is”运算符

时间:2014-01-21 14:54:55

标签: c# oop design-patterns double-dispatch multimethod

我有以下C#代码。它工作正常;但GetDestination()方法使用is operator混乱了多个if条件。

在.Net 4.0(或更高版本)中,避免这些“if”条件的最佳方法是什么?

编辑:角色是业务模型的一部分,目标纯粹是使用该业务模型的一个特定应用程序的工件。

CODE

public class Role { }
public class Manager : Role { }
public class Accountant : Role { }
public class Attender : Role { }
public class Cleaner : Role { }
public class Security : Role { }

class Program
{
    static string GetDestination(Role x)
    {
        string destination = @"\Home";

        if (x is Manager)
        {
            destination = @"\ManagerHomeA";
        }

        if (x is Accountant)
        {
            destination = @"\AccountantHomeC";
        }

        if (x is Cleaner)
        {
            destination = @"\Cleaner";
        }

        return destination;

    }

    static void Main(string[] args)
    {
        string destination = GetDestination(new Accountant());
        Console.WriteLine(destination);
        Console.ReadLine();
    }
}

参考

  1. Dictionary<T,Delegate> with Delegates of different types: Cleaner, non string method names?
  2. Jon Skeet: Making reflection fly and exploring delegates
  3. if-else vs. switch vs. Dictionary of delegates
  4. Dictionary with delegate or switch?
  5. Expression and delegate in c#

7 个答案:

答案 0 :(得分:5)

在派生类中重写virtual属性应该可以解决问题:

class Role
{
    public virtual string Destination { get { return "Home"; } }
}
class Manager : Role
{
    public override string Destination { get { return "ManagerHome;"; } }
}
class Accountant : Role
{
    public override string Destination { get { return "AccountantHome;"; } }
}
class Attender : Role
{
    public override string Destination { get { return "AttenderHome;"; } }
}
class Cleaner : Role
{
    public override string Destination { get { return "CleanerHome;"; } }
}
class Security : Role { }

我没有使属性抽象,在派生类中没有覆盖时提供默认的Home值。

用法:

string destination = (new Accountant()).Destination;
Console.WriteLine(destination);
Console.ReadLine();

答案 1 :(得分:5)

这是一个选项:

private static readonly Dictionary<Type, string> DestinationsByType =
    new Dictionary<Type, string> 
{
    { typeof(Manager), @"\ManagerHome" },
    { typeof(Accountant), @"\AccountantHome" },
    // etc
};

private static string GetDestination(Role x)
{
    string destination;
    return DestinationsByType.TryGetValue(x.GetType(), out destination)
        ? destination : @"\Home";
}

注意:

  • 这不能处理空参数。目前尚不清楚你是否真的需要它。您可以轻松添加空值处理。
  • 这不会与继承一起复制(例如class Foo : Manager);你可以通过在必要时上升继承层次结构来做到这一点

这是一个 处理这两个点的版本,代价是复杂性:

private static string GetDestination(Role x)
{
    Type type = x == null ? null : x.GetType();
    while (type != null)
    {
        string destination;
        if (DestinationsByType.TryGetValue(x.GetType(), out destination))
        {
            return destination;
        }
        type = type.BaseType;
    }
    return @"\Home";
}

编辑:如果Role本身具有Destination属性,那将更加清晰。这可以是虚拟的,也可以由Role基类提供。

然而,可能是目的地真的不是Role应该关注的东西 - 可能是Role是商业模式的一部分,目的地纯粹是人工制品使用该业务模型的一个特定应用程序在这种情况下,你不应该将它放入Role,因为这会打破关注点的分离。

基本上,在不了解更多背景的情况下,我们无法确定哪种解决方案最合适 - 在设计方面往往是如此。

答案 2 :(得分:4)

方法1(已选中):使用dynamic关键字实施multimethods / double dispatch

方法2 :使用dictionary来避免{SAMPet'在以下答案中提到的if个阻止。

方法3:如果条件不是相等,则使用HashListdelegates(例如,如果输入&lt; 25)。请参阅how to refactor a set of <= , >= if...else statements into a dictionary or something like that

Apporach 4:虚拟功能如MarcinJuraszek在下面的回答中所述。

使用动态关键字

的MultiMethods / Double Dispatch方法

原理:此处算法会根据类型进行更改。也就是说,如果输入是Accountant,则要执行的功能与Manager不同。

    public static class DestinationHelper
    {
        public static string GetDestinationSepcificImplm(Manager x)
        {
            return @"\ManagerHome";
        }

        public static string GetDestinationSepcificImplm(Accountant x)
        {
            return @"\AccountantHome";
        }

        public static string GetDestinationSepcificImplm(Cleaner x)
        {
            return @"\CleanerHome";
        }
    }

   class Program
    {
        static string GetDestination(Role x)
        {

            #region Other Common Works
            //Do logging
            //Other Business Activities
            #endregion

            string destination = String.Empty;
            dynamic inputRole = x;
            destination = DestinationHelper.GetDestinationSepcificImplm(inputRole);
            return destination;
        }

        static void Main(string[] args)
        {
            string destination = GetDestination(new Security());
            Console.WriteLine(destination);
            Console.WriteLine("....");
            Console.ReadLine();
        }

    }

答案 3 :(得分:2)

这是一种强类型的命令式语言,因此if语句和类型检查将会发生。

话虽如此,您是否考虑过virtual上的Role方法可以覆盖以提供目的地string

另一种选择,查找表!

Dictionary<Type, string> paths = new Dictionary<TYpe, string>()
{
    { typeof(Manager),  @"\ManagerHomeA" }
    { typeof(Accountant),  @"\AccountantHomeC" }
    { typeof(Cleaner),  "Cleaner" }
}

string path = @"\Home";
if(paths.ContainsKey(x.GetType())
    path = paths[x];

答案 4 :(得分:1)

角色应该有一个返回目标的虚函数:

public virtual string GetDestination()
{
     return "Home";
}

并且所有类都应该覆盖此函数并返回正确的字符串。然后在代码中你会有:

var role = new Accountant();
string destination = role.GetDestination();

我希望有所帮助。可能有拼写错误,我正在写作。

答案 5 :(得分:1)

一种方法是使用地图而不是if:

//(psuedocode)
private Dictionary<Type, string> RoleMap;

void SomeInitializationCodeThatRunsOnce()
{
  RoleMap.Add(typeof(Manager), @"\ManagerHome");
  RollMap.Add(typeof(Accountant), @"\AccountantHome");
  // ect...
}

string GetDestination(Role x)
{
  string destination;
  if(!RoleMap.TryGet(x.GetType(), out destination))
    destination = @"\Home";
  return destination;
}

进一步阅读:http://www.hanselman.com/blog/BackToBasicsMovingBeyondForIfAndSwitch.aspx

答案 6 :(得分:0)

您可以使用接口定义或抽象方法/属性

with interface:

public interface IDestinationProvider
{
    sting Destination { get; }
}

string GetDestination(Role role)
{
    var provider = role as IDestinationProvider;
    if (provider != null)
        return provider.Destination;
    return "Default";
}

带有抽象基类

abstract class Role 
{ 
    public abstract string GetDestination();
}

class Manager : Role
{
    public virtual string GetDestination() { return "ManagerHomeA"; }
}

string GetDestination(Role role)
{
    return @"\" + role.GetDestination();
}

或属性:

[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class DestinationAttribute : Attribute
{
    public DestinationAttribute() { this.Path = @"\Home"; }
    public string Path { get; set; }
}

[Destination(Path = @"\ManagerHome")]
public class Manager : Role { }

string GetDestination(Role role)
{
    var destination = role.GetType().GetCustomAttributes(typeof(DestinationAttribute), true).FirstOrDefault();
    if (destination != null)
        return destination.Path;

    return @"\Home";
}