首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >用于多个打印机属性的API

用于多个打印机属性的API
EN

Code Review用户
提问于 2019-05-18 19:40:02
回答 1查看 80关注 0票数 3

我有不同属性的网络打印机(例如支持的网络协议、语言、状态、打印模式)。

代码语言:javascript
运行
复制
public abstract class PrinterAttribute
{
  protected PrinterAttribute(int value, string dsc)
  {
    this.Value = value;
    this.Dsc = dsc;
  }

  protected PrinterAttribute(int value, string dsc, Enum type) : this(value, dsc)
  {
    this.Type = type;
  }

  protected int Value { get; private set; }
  protected string Dsc { get; private set; }
  protected Enum Type { get; private set; }
}

最后,我将有许多类,如NetworkProtocolAttributePrinterModeAttribute

代码语言:javascript
运行
复制
public class NetworkProtocolAttribute : PrinterAttribute
{
  public NetworkProtocolAttribute(int value, string dsc) : base(value, dsc) {}
  public NetworkProtocolAttribute(int value, string dsc, ProtocolType protocolType) : base(value, dsc, protocolType) {}

  #region 1. way - get desired network protocol from locating it in all possible ones
  // all possible network protocols that a printer can support
  private static IList<NetworkProtocolAttribute> allPossibleProtocols;

  private static IList<NetworkProtocolAttribute> AllPossibleProtocols
  {
    get
    {
      if(allPossibleProtocols == null)
      {
        allPossibleProtocols = new List<NetworkProtocolAttribute>()
        {
          new NetworkProtocolAttribute(0, "None"),
          new NetworkProtocolAttribute(1, "FTP"),
          new NetworkProtocolAttribute(2, "LPD"),
          new NetworkProtocolAttribute(4, "TCP"),
          new NetworkProtocolAttribute(8, "UDP"),
          new NetworkProtocolAttribute(0x10, "HTTP"),
          new NetworkProtocolAttribute(0x20, "SMTP"), // simple mail transfer protocol
          new NetworkProtocolAttribute(0x40, "POP3"),
          new NetworkProtocolAttribute(0x80, "SNMP"), // simple network management protocol
          new NetworkProtocolAttribute(0x100, "Telnet"),
          new NetworkProtocolAttribute(0x200, "Weblink"),
          new NetworkProtocolAttribute(0x400, "TLS"),
          new NetworkProtocolAttribute(0x800, "HTTPS")
        };
      }

      return allPossibleProtocols;
    }
  }


  public static NetworkProtocolAttribute SMTP_FirstWay
  {
    get
    {
      // don't like it because you locate it by value
      return AllPossibleProtocols.Single(x => x.Value == 0x80);
    }
  }
  #endregion


  #region 2. way - create public static NetworkProtocol for every possible protocol
  private static NetworkProtocolAttribute smtp;
  public static NetworkProtocolAttribute SMTP_SecondWay
  {
    get
    {
      if( smtp == null)
      {
        smtp = new NetworkProtocolAttribute(0x80, "SNMP");
      }

      return smtp;
    }
  }
  #endregion

  #region 3. way - create enum and find it by enum
  public enum ProtocolType : int
  {
    None,
    FTP,
    LPD,
    TCP,
    UDP,
    HTTP,
    SMTP,
    POP3,
    SNMP,
    Telnet,
    Weblink,
    TLS,
    HTTPS
  }

  private static IList<NetworkProtocolAttribute> allPossibleProtocols3;
  protected static IList<NetworkProtocolAttribute> AllPossibleProtocols3
  {
    get
    {
      if (allPossibleProtocols3 == null)
      {
        allPossibleProtocols3 = new List<NetworkProtocolAttribute>()
        {
          new NetworkProtocolAttribute(0,    "None", ProtocolType.None),
          new NetworkProtocolAttribute(1,    "FTP", ProtocolType.FTP),
          new NetworkProtocolAttribute(2,    "LPD", ProtocolType.LPD),
          new NetworkProtocolAttribute(4,    "TCP", ProtocolType.TCP),
          new NetworkProtocolAttribute(8,    "UDP", ProtocolType.UDP),
          new NetworkProtocolAttribute(0x10, "HTTP", ProtocolType.HTTP),
          new NetworkProtocolAttribute(0x20, "SMTP", ProtocolType.SMTP),
          new NetworkProtocolAttribute(0x40, "POP3", ProtocolType.POP3),
          new NetworkProtocolAttribute(0x80, "SNMP", ProtocolType.SNMP),
          new NetworkProtocolAttribute(0x100, "Telnet", ProtocolType.Telnet),
          new NetworkProtocolAttribute(0x200, "Weblink", ProtocolType.Weblink),
          new NetworkProtocolAttribute(0x400, "TLS", ProtocolType.Telnet),
          new NetworkProtocolAttribute(0x800, "HTTPS", ProtocolType.HTTPS)
        };
      }

      return allPossibleProtocols3;
    }
  }

  public static NetworkProtocolAttribute GetProtocol_ThirdWay(ProtocolType protocolType)
  {
    NetworkProtocolAttribute match = AllPossibleProtocols3.Where(x => x.Type.Equals(protocolType)).SingleOrDefault();

    return match;
  }
  #endregion

最后,如何通过网络打印机获得所需的协议支持:

代码语言:javascript
运行
复制
class Program
{
  static void Main(string[] args)
  {
    // 1. way
    NetworkProtocolAttribute protocol1 = NetworkProtocolAttribute.SMTP_FirstWay;

    // 2. way
    NetworkProtocolAttribute protocol2 = NetworkProtocolAttribute.SMTP_SecondWay;

    // 3. way
    NetworkProtocolAttribute protocol3 = NetworkProtocolAttribute.GetProtocol_ThirdWay(
     NetworkProtocolAttribute.ProtocolType.SMTP);

    Console.ReadKey();
}

我喜欢的是1和3。方法是,您看到了所有可能的协议的列表。3. way也只有一种获得协议的方法,而2. way对于每种协议都有一种方法。

EN

回答 1

Code Review用户

发布于 2019-05-20 10:05:15

自定义与普通枚举

比较

这看起来像是某种自定义枚举类型,但以它当前的形式来说,它并不是很有用:

  • PrinterAttribute中的所有属性都是protected,这意味着只有派生类才能访问它们。这对其他代码不太有用。
  • Enum类型的属性使用起来很麻烦,因为它的类型太弱。它只告诉您它是一个枚举,而不是哪个枚举,因此您仍然不知道哪些值是有效的。
  • Enum值已经可以给出一个特定的数值,因此您不需要该Value属性。
  • NetworkProtocolAttribute.FTP == new NetworkProtocolAttribute(1, "FTP")失败了,因为它们不是同一个实例。调用Equals也是如此。您将希望实现相关的操作符,并覆盖标准的Equals方法(以及使用它,也包括GetHashCode)。实现IEquatable<T>也是一个好主意。

你考虑过用普通的枚举代替吗?

代码语言:javascript
运行
复制
[Flags]     // Indicates that this enum is intended to be used as a bitmask
public enum Protocol    // No need to specify int as underlying type
{
    None =  0x00,
    FTP =   0x01,
    LPD =   0x02,
    TCP =   0x04,
    UDP =   0x08,
    HTTP =  0x10,
    SMTP =  0x20,
    // and so on
}

// Use:
var protocol = Protocol.FTP;
protocol.ToString();  // -> "FTP"

var protocols = Protocol.FTP | Protocol.UDP;
protocols.HasFlag(Protocol.UDP);  // -> true

Enum.GetValues(typeof(Protocol)); // -> Protocol[] { None, FTP, LDP, ... }

如果确实需要,可以向这些值添加[Description("...")]属性,但是枚举名称本身已经足够描述性了。如果您将这些描述用于显示目的,那么最好在UI层(由于本地化等原因)这样做。

其他笔记

  • 不要将IList<T>用于静态只读属性:它允许其他代码修改列表。改用IEnumerable<T>IReadOnlyCollection<T>IReadOnlyList<T>。另外,使用实际的ReadOnlyCollection<T> (List<T>有一个方便的AsReadOnly方法)。
  • 属性可以直接初始化:Foo MyProperty { get; } = new Foo();,因此不需要显式定义字段并在getter中执行空检查。顺便说一句,这种空检查方法并不是线程安全的,所以如果您确实需要延迟初始化,可以使用Lazy<T>LazyInitializer
  • NetworkProtocolAttribute protocol1 = NetworkProtocolAttribute.SMTP_FirstWay;可缩短为var protocol1 = NetworkProtocolAttribute.SMTP_FirstWay;。这样编译器就可以推断类型,所以如果从右边已经很明显的话,就不需要写两次类型了。
  • .Where(...).FirstOrDefault()可以简化为.FirstOrDefault(...)
  • 就我个人而言,我不喜欢使用this.。属性是用PascalCase编写的,参数是用camelCase编写的,所以Value = value;对我来说很清楚。
  • 用于只读的属性不需要设置设置器。
  • 只需写出Description完全可读性是很重要的,特别是从长远来看,在自动完成工具的帮助下编写并不需要那么多时间。
  • 类的名称表明它们是属性--用于将元数据附加到其他代码的特殊类型。因为您不是从System.Attribute继承的,这显然不是您想要的,所以您可能需要选择不同的名称以避免混淆。

最后,关于#1和#3中的线性查找,您可以通过反转列表和其他属性之间的关系来消除这些查询:

代码语言:javascript
运行
复制
public static NetworkProtocolAttribute FTP { get; } = new NetworkProtocolAttribute(1, "FTP");
public static NetworkProtocolAttribute LPD { get; } = new NetworkProtocolAttribute(2, "LPD");
// and so on...

public static IReadOnlyList<NetworkProtocolAttribute> AllProtocols { get; }
    = new List<NetworkProtocolAttribute> {
        FTP,
        LPD,
        // and so on...
    }.AsReadOnly();

但是,除非你有一些具体的要求,否则我只会使用普通的枚举。

票数 1
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/220484

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档