【问题标题】:Preprocessor : Get Operating System .Net Core预处理器:获取操作系统 .Net Core
【发布时间】:2018-09-12 12:41:21
【问题描述】:

我正在编写一个希望在 Windows 和 Linux 上使用的类。

此类中的一种方法是访问Windows Registry

我希望实现的是在使用 Linux 机器时以某种方式禁用此特定方法。

首先,我做了一些研究,看看是否有针对 @ 的东西987654322@ 可以让我检查正在使用的操作系统,我找到了this 并且确实有效。

当我在访问方法时将它实现到我的代码中时,我希望禁用访问 Windows 注册表的方法,但最接近的方法是使用 switch 语句,类似这样

switch (OS)
{
    case OSX:
    return;

    case LINUX:
    return
}

return,如果操作系统不受支持,这可行,但是我认为将其全部禁用会更好,而不是为该特定方法不支持的操作系统抛出错误
然后我继续查看preprocessor directives,认为如果我能够根据框架等检测和禁用部分代码,也许我可以使用类似的东西来禁用部分代码,具体取决于操作这样,即使尝试访问该方法,它们也永远不会被调用

我从那里继续查看是否可以使用 preprocessor directives 禁用部分代码。
我找到了 this
我知道它适用于C++,但它似乎是我能找到的最接近我在.Net Core 中尝试实现的目标

在一个完美的世界中,它看起来像这样的

    /// <summary>
    /// Get the file mime type
    /// </summary>
    /// <param name="filePathLocation">file path location</param>
    /// <returns></returns>
    `#if WINDOWS`
    public static string GetMimeType(this string filePathLocation)
    {
        if (filePathLocation.IsValidFilePath())
        {
            string mimeType = "application/unknown";
            string ext = Path.GetExtension(filePathLocation).ToLower();
            Microsoft.Win32.RegistryKey regKey = Microsoft.Win32.Registry.ClassesRoot.OpenSubKey(ext);

            if (regKey != null && regKey.GetValue("Content Type") != null)
            {
                mimeType = regKey.GetValue("Content Type").ToString();
            }
            return mimeType;
        }
        return null;
    }
`#endif`

我确实看到了#Define,所以我尝试了类似#define IS_WINDOWS 的方法,并将它与#if IS_WINDOWS 一起添加到我的班级中 但是,如果我希望一遍又一遍地重用静态类,我看不到如何更改该值。

【问题讨论】:

  • 您能否在其他方面解释一下您已经找到的工作方法中您不喜欢什么?老实说,我不明白你为什么不想使用它。另外我不明白你为什么要使用预处理器指令。这些指令在编译开始之前定义并提供给编译器。所以也许我错了,但似乎你将无法在 Windows 上编译你的程序并在 Linux 上运行它。在这种情况下,您的 #if WINDOWS 将是真实的,而实际上不是......

标签: c# .net-core preprocessor-directive


【解决方案1】:

虽然您可以追求涉及#define 的路线,但它是编译时的,您将失去很多 .Net 的多平台优势。您还必须兼顾多种配置、多种构建等。

在可能的情况下,将依赖于平台的行为隐藏在独立于平台的抽象后面,并在运行时使用System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform 进行检查:

interface IPlatform
{
    void DoSomething();
}

class WindowsImpl : IPlatform
{
    public void DoSomething()
    {
        // Do something on Windows
    }
}

class LinuxImpl : IPlatform
{
    public void DoSomething()
    {
        // Do something on Linux
    }
}

// Somewhere else
var platform = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? new WindowsImpl() : new LinuxImpl();
platform.DoSomething();

这适用于许多事情,包括PInvoke。您将能够在任一平台上使用相同的二进制文件,并且以后添加 OSX 会更容易。

如果您需要在编译时隔离与平台相关的代码(可能包仅适用于 Windows),MEF2/System.Composition 可以帮助您制作一个插件框架,其中每个平台都有自己的程序集:

// In Windows.dll class library project
using System.Composition;

[Export(typeof(IPlatform))]
public class WindowsImpl : IPlatform
{
    public void DoSomething()
    {
        //...
    }
}

然后在你的主程序中:

using System.Composition.Hosting;

var configuration = new ContainerConfiguration();
var asm = Assembly.LoadFrom(pathToWindowsDll);
configuration.WithAssembly(asm);
var host = configuration.CreateContainer();
var platform = host.GetExports<IPlatform>().FirstOrDefault();

【讨论】:

  • 嗨,Jake,感谢您的回复,我在理解您的答案时遇到了一些麻烦,我有一个静态类,其中包含一堆 .net 核心的扩展助手。其中一个扩展通过访问注册表来获取文件的 mime 类型,这在 linux 和 mac 中是完全不可能的,我看不到接口如何提供帮助,因为我只有一个类与该方法,有什么我遗漏的吗?或者您的想法是如何与这两个类进行接口并在两者之间重复代码?再次感谢
  • 我是说您可能要考虑使用运行时检查来选择正确的行为,而不是在编译时进行。使用您的GetMimeType() 功能。当然,Windows 实现可能会使用注册表,但也可能会有使用其他东西的 Linux 实现。我手边没有 Linux VM,但该代码可以在 OSX 上编译。它在运行时在OpenSubKey() 上使用System.Security.SecurityException 失败,但关键是您不需要在任何地方都使用#ifdef(包括调用该函数的所有地方)。
  • 如果你真的想“禁用”它而不是处理所有调用它的地方,你也可以使用ConditionalAttribute
  • 谢谢杰克,是的,我确定确实有一些适用于 linux 的东西,我也许可以实现其他东西,我必须做一些研究,是的,我确实看到了 ConditionalAttribute 但是,在你编译之前,我认为你需要做这样的事情set OS_WINDOWS = 1 为了只使用 Windows 端,我试图让它一个 dll 来统治它们,我可能需要创建不同的包,我在这个阶段不知道,我猜我们会看到的。
猜你喜欢
  • 2015-07-21
  • 1970-01-01
  • 2013-09-25
  • 1970-01-01
  • 2016-12-11
  • 2018-03-14
  • 1970-01-01
  • 2012-04-06
相关资源
最近更新 更多