使用外部DLL中的枚举

时间:2012-06-07 21:14:18

标签: c# reflection dll enums

我有一个我正在工作的项目,它将涉及创建一个将在多个其他站点中使用的DLL。在这个DLL中,我们需要引用大约10个枚举。但是,对于使用DLL的每个站点,这些枚举的值将不同。例如:

MyBase.dll可能有一个MyClass类,其属性类型为MyEnum。

然后在MySite中引用MyBase.dll。 MyStie还会引用MyEnums.dll,它将包含MyEnum类型的值。

有没有办法实现这个目标?在构建MyBase.dll时,我知道MyEnums.dll中存在哪些枚举。问题是我无法在没有专门引用MyEnums.dll的情况下构建MyBase.dll,MyEnums.dll只有在特定项目中使用MyBase.dll才会创建。

我希望这是有道理的,希望我能在这里找到答案。

感谢。

编辑:

感谢所有评论。这需要一些阅读才能完全理解,但让我试着给出一个更好的例子来说明我在这里看到的内容。

让我们说下面的代码在我的DLL中,将被放入各种项目中。状态是一个枚举。

public Class MyClass
{
    private Status _currentStatus;

    public Status CurrentStatus
    {
        get
        {
            return _currentStatus;
        }
    }

    public void ChangeStatus(Status newStatus)
    {
        _currentStatus = newStatus;
    }
}

我希望能够在各个项目中定义Status的可能值。所以在这个DLL中,我永远不会引用Status enum中的值,我只需要知道它存在。

我希望我能做的更清楚。

2 个答案:

答案 0 :(得分:2)

如果您希望每个客户端看到不同的枚举值(在不同的汇编版本中),那么使用枚举是一个糟糕的解决方案 - 更改将破坏客户端代码......

使用枚举可能工作(只要枚举名称和程序集名称相同且程序集未签名) - 您只需交换程序集即可。但是,如果在代码中任何位置使用的值都不在最后,那么最终会出现异常。此外,您可以明确地对值进行编号,以确保值的不同子集不会以不同值的相同数字或相同值的不同数字结束。

而是考虑使用动态构建的集合,例如列表,字典或数据库表。 或者只是向每个人提供具有相同的枚举值超集的相同程序集,并让用户决定哪些值与它们相关(可能使用值的重要前缀作为约定)。

或者你可以使用两者的组合......

生成不同的结构(不同的类型名称(或名称空间)和程序集名称)每个站点具有不同的属性(根据站点的配置文件)和一个主结构接受结构的服务。让所有结构实现相同的接口,您期望接收...

public interface IStatus
{
    string GetKey();
}

public struct ClientXStatus : IStatus
{
    private readonly string _key;

    private ClientXStatus(string key)
    {
        _key = key;
    }

    // Don't forget default for structs is 0,
    // therefore all structs should have a "0" property.
    public ClientXStatus Default
    {
        get
        {
            return new ClientXStatus();
        }
    }

    public ClientXStatus OptionB
    {
        get
        {
            return new ClientXStatus(10);
        }
    }

    string IStatus.GetKey()
    {
        return _key;
    }

    public override bool Equals(object obj)
    {
        return (obj is IStatus) && ((IStatus)obj).GetKey() == _key;
    }

    public override int GetHashCode()
    {
        return _key.GetHashCode();
    }

    public static bool operator==(ClientXStatus x, IStatus y)
    {
        return x.Equals(y);
    }

    public static bool operator==(IStatus x, ClientXStatus y)
    {
        return y.Equals(x);
    }

    public static bool operator!=(ClientXStatus x, IStatus y)
    {
        return !x.Equals(y);
    }

    public static bool operator!=(IStatus x, ClientXStatus y)
    {
        return !y.Equals(x);
    }

    // Override Equals(), GetHashCode() and operators ==, !=
    // So clients can compare structures to each other (to interface)
}

为服务使用主结构:

public struct MasterStatus : IStatus
{
    private readonly string _key;

    private MasterStatus(string key)
    {
        _key = key;
    }

    // Don't forget default for structs is 0,
    // therefore all structs should have a "0" property.
    public MasterStatus Default
    {
        get
        {
            return new MasterStatus();
        }
    }

    // You should have all the options here
    public MasterStatus OptionB
    {
        get
        {
            return new MasterStatus(10);
        }
    }

    // Here use implicit interface implementation instead of explicit implementation
    public string GetKey()
    {
        return _key;
    }

    public static implicit operator MasterStatus(IStatus value)
    {
        return new MasterStatus(value.GetKey());
    }

    public static implicit operator string(MasterStatus value)
    {
        return new value._key;
    }

    // Don't forget to implement Equals, GetHashCode,
    // == and != like in the client structures
}

演示服务代码:

public void ServiceMethod(IStatus status)
{
    switch (status.GetKey())
    {
        case (string)MasterStructA.OptionB:
            DoSomething();
    }
}

或者:

public void ChangeStatus(IStatus status)
{
    _status = (MasterStatus)status;
}

这样你:

  1. 使用代码生成来防止值冲突。

  2. 强制用户通过隐藏值(作为私有)并仅接受您的结构来使用编译时检查(无int值或字符串值)。

  3. 在服务代码(接口)中使用真正的多态,而不是容易出错的黑客。

  4. 使用不可变值类型(如枚举)而不是引用类型。

答案 1 :(得分:0)

首先,您必须决定放置常量的WHERE。然后,您可以将enum转换为静态属性。

例如:

public enum MyEnum
{
    Value1,
    Value2
}

可以改为(第一个天真的方法):

public static class MyFakeEnum
{
    public static int Value1
    {
        get { return GetActualValue("Value1"); }
    }

    public static int Value2
    {
        get { return GetActualValue("Value2"); }
    }

    private static int GetActualValue(string name)
    {
        // Put here the code to read the actual value
        // from your favorite source. It can be a database, a configuration
        // file, the registry or whatever else. Consider to cache the result.
    }
}

这将简单地提供所需的常量,但如果您需要MyFakeEnum作为参数,则必须抛弃该类型的编译时检查。为了获得更好的解决方案,您可以遵循,例如,Microsoft对System.Drawing.Color所做的(或多或少)。

public sealed class MyFakeEnum
{
    public static readonly MyFakeEnum Value1 = new MyFakeEnum("Value1");
    public static readonly MyFakeEnum Value2 = new MyFakeEnum("Value2");

    private MyFakeEnum(string name)
    {
        _name = name;
    }

    public static implicit operator int(MyFakeEnum value)
    {
        return GetActualValue(value._name);
    }

    private string _name;
}

当然,您应该至少为EqualsGetHashCodeToString提供适当的覆盖。

<强>临

  • 它可以是现有enum的升级版。代码不会被破坏,您可能只需要重新编译。
  • 您可以将其用作强类型参数。例如:void DoSomething(MyFakeEnum value)有效且调用者无法传递其他内容(请注意,这是其中一个原因,因为枚举被视为弱)。
  • 如果您实现了所有必需的运算符,则可以使用常规语法进行比较:value == MyFakeEnum::Value1
  • 使用一点代码,您甚至可以实现FlagsAttribute语法。
  • 您不会更改枚举的正常语法:MyFakeEnum.Value1
  • 您可以在类型中实现任意数量的隐式/显式转换运算符,任何转换都是安全的,并且在完成时检查(使用标准枚举时不会这样)。
  • 您没有可以被更改破坏的硬编码字符串,并且在它们导致运行时错误(是,运行时)之前不会被捕获。例如,如果您要更改定义,则使用字典,然后您必须搜索所有代码以查找该字符串。

<强>缺点

  • 首次实施时间较长,因为您必须编写支持代码(但对于任何新值,您只需添加新行)。
  • 值列表是固定的,必须在编译时知道(如果您正在搜索enum的替代品,这不是问题,因为它也已修复)。

使用此解决方案,您可以保存或多或少与标准枚举相同的语法。