3

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

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

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

有没有办法做到这一点?在构建 MyBase.dll 时,我知道 MyEnums.dll 中将存在哪些枚举。问题是我无法在没有专门引用 MyEnums.dll 的情况下构建 MyBase.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 枚举中可能存在的值,我只需要知道它存在即可。

我希望这对我正在尝试做的事情更加清楚。

4

2 回答 2

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. 在服务的代码(接口)中使用真正的多态性,而不是容易出错的 hack。

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

于 2012-06-07T21:18:12.847 回答
0

首先,您必须决定将常量放在何处。然后您可以将您的属性转换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作为参数,则必须放弃对类型的编译时检查。对于更好的解决方案,您可以遵循,例如,微软(或多或少)为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;
}

当然,您至少应该为 和 提供适当EqualsGetHashCode覆盖ToString


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

缺点

  • 第一个实现时间更长,因为您必须编写支持代码(但对于任何新值,您只需添加一个新行)。
  • 值列表是固定的,并且必须在编译时知道(如果您正在搜索替换,这不是问题,enum因为它也是固定的)。

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

于 2012-06-07T21:20:48.200 回答