将 Visual Studio 的代码片段导出到 VS Code

导语

和原文作者一样,水弟我现在也是使用 VS Code 和 Rider 作为主力开发工具,尤其是 VS Code 可以跨平台,又有丰富的插件支持和多种编程语言支持。当我从 VS 转移到以 VS Code 的开发过程中,遇到的最大问题就是代码提示的不完善(被 VS 和 R# 调教坏了,总想按 tab 键)。当我看到原文作者通过从 VS 中导出代码片段到 VS Code 时,瞬间被吸引到了。 虽然在不知不觉中用了 VS 自带的代码片段,但我从来没有想过要自定义专属的代码片段,最多也是制作用于项目的模板(方便创建特定的类型文件)。虽然导出到 Rider 不是很完美,但 Rider 自带了 R#,对这方面的需求还是很少的。


译文:

Visual Studio 内置了非常好用的代码片段工具,多年来我一直在使用它来创建大量有用的扩展片段,使我的日常开发更容易。我有很多 C# 代码片段,但更多的是用于 HTML 、自定义的 Bootstrap 代码片段,乃至复杂的 HTML 控件代码段。偶尔也会用到 JavaScript 、XAML 甚至Powershell 。

在过去的几年里,我越来越多地使用其他工具与 Visual Studio 结合使用。特别是Visual Studio Code和JetBrains Rider。

在多年的使用 Visual Studio 中,我已经累积了 130 多个代码片段。每当我在其他开发环境中工作时 ( VS Code 或者 Rider),我真的很需要他们,特别是要写一大段 HTML的时候,总是要去痛苦地去对应的文档站点查找。使用代码片段功能,只需几次击键就会自动填充我自定义的特定代码,每天可节省大量时间。

所以我很需要代码片段功能,有时我打开 Visual Studio 只是为了找到需要的 HTML 的代码片段,然后将它们粘贴回 VS Code 或 Rider。虽然繁琐,但是仍然从文档网站中复制代码,然后手动修改代码来得便捷。如果能在每个对应的开发环境中直接执行代码片段的功能,那就太好了!

因此,在过去的几个周末,我做了一个将 Visual Studio 中的代码片段导出到 VS Code 中的小工具,同时尽量能导出到 JetBrains Rider 。

如果你感兴趣,可以在GitHub上找到代码:

  • GitHub上的VisualStudioSnippetConverter

另外说一句,这还只是一个菜鸟项目,并不能保证它支持所有类型的的代码片段。只是我自己拥有的 137 个代码片段都完美地移植到 VS Code,并且能够运行。同时我还可以重新导出, 轻松地导出新创建的代码片段,这样就可以对比和更新了。

对于 Rider 而言,操作起来更为复杂,因为 Rider 有一种疯狂的机制,可以将模板存储在内部的单个配置文件中。它还为 .NET相关的片段 (C#、VB、F#、Razor、ASP.NET )和 基于 Web ( html、css、js 等)的代码片段使用了多个完全不同的存储引擎。所以工具目前仅支持一次性导出 .NET 相关代码段,因为 Rider 中基于 GUID 的密钥系统不允许在没有 GUID 的情况下查找现有代码段。后面我们再详细介绍。

代码片段转换器

640?wx_fmt=png

你可以通过借助 .NET 全局工具 (.NET Global SDK Tool ),使用 Nuget 下载和运行代码片段转换器:

dotnet tool install --global dotnet-snippetconverter

如果您不想安装并只运行该工具,您可以克隆或下载Github仓库,然后:

cd .\SnippetConverter\
dotnet run

安装后, 可以通过指向文件夹或单个文件将 Visual Studio 中的代码片段批量或单独转换为 VS Code 支持的代码片段。

snippetconverter ~2017 -r -d

或者,您可以像下面这张屏幕截图那样指定输出文件:

640?wx_fmt=png

有几个选项可用于转换单个片段和文件夹,使用前缀,递归文件夹,输出生成文件的路径等:

Syntax:-------SnippetConverter <sourceFileOrDirectory> -o <outputFile> --mode --prefix --recurse --displayCommands:---------HELP || /?          This help display           Options:--------sourceFileOrDirectory  Either an individual snippet file, or a source folderOptional special start syntax using `~` to point at User Code Snippets folder:~      -  Visual Studio User Code Snippets folder (latest version installed)~2017  -  Visual Studio User Code Snippets folder (specific VS version 2019-2012)                       -o <outputFile>        Output file where VS Code snippets are generated into (ignored by Rider)   Optional special start syntax using `~` to point at User Code Snippets folder:%APPDATA%\Code\User\snippets\ww-my-codesnippets.code-snippets~\ww-my-codesnippets.code-snippets                       if omitted generates `~\exported-visualstudio.code-snippets`-m,--mode              vs-vscode  (default)vs-rider   experimental - (C#,VB.NET,html only)
-d                     display the target file in Explorer
-r                     if specifying a source folder recurses into child folders
-p,--prefix            snippet prefix generate for all snippets exportedExample: `ww-` on a snippet called `ifempty` produces `ww-ifempty`Examples:---------# vs-vscode: Individual Visual Studio Snippet
SnippetConverter "~2017\Visual C#\My Code Snippets\proIPC.snippet" -o "~\ww-csharp.code-snippets" -d# vs-vscode: All snippets in a folder user VS Snippets and in recursive child folers
SnippetConverter "~2017\Visual C#\My Code Snippets" -o "~\ww-csharp.code-snippets" -r -d# vs-vscode: All the user VS Snippets and in recursive child folders
SnippetConverter ~2017\ -o "~\ww-all.code-snippets" -r -d# vs-vscode: All defaults: Latest version of VS, all snippets export to  ~\visualstudio-export.code-snippets
SnippetConverter ~ -r -d --prefix ww-# vs-rider: Individual VS Snippet
SnippetConverter "~2017\proIPC.snippet" -m vs-rider -d# vs-rider: All VS Snippets in a folder
SnippetConverter "~2017\Visual C#\My Code Snippets" -m vs-rider -d

上面的用例应该足够说明用途了。如果还想要了解更多信息,请接着往下看......

什么是 VS Code 的代码片段

如果您不熟悉或不使用代码片段,那您并不是少数人。它们在 Visual Studio 中几乎是一个隐藏的功能,这是一个耻辱,因为它们是非常有用的生产力工具。不幸的是,Visual Studio 没有任何有用的内置UI来创建这些片段,因此大多数开发人员都没有充分利用此功能。Visual Studio 只能蹩脚地点击 ** 工具 - > 代码片段管理器 ** 菜单 ,除了一个查看器之外,它没有其他管理功能,仅仅是查看哪些片段是可用的,没有内置的方法来创建或编辑片段,甚至跳转到并查看代码片段。

但是,代码片段仅仅只是位于用户目录的 Documents 文件夹下的 XML 文件。它们非常容易创建和更新,仅仅是原始的 XML 文件,用 VS Code 等文本编辑器去做代码片段和高亮实在是非常简单。尽管在 Visual Studio 中有一些提供 UI 操作的劣质插件,但它们往往比原始的代码片段文件更麻烦。

创建新代码段的最佳方法是复制现有代码段并对其进行修改以满足您的需求。

一般来说,代码片段位于 (水弟我是直接用 Everything搜索的):

<Documents>\Visual Studio 2017\Code Snippets

每种语言技术都有自己的子文件夹进行分组,但仅仅是文件夹上的区分而已。代码片段实际上通过 XML中的 Language 属性确定它们适用的语言。

Visual Studio在此位置附带了许多代码段,您可以使用这些代码段作为新代码段的模板进行学习。

<?xml version="1.0" encoding="utf-8"?><CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet"><CodeSnippet Format="1.0.0"><Header><Title>Property with INotifyPropertyChange raised</Title><Description>Control Property with Attributes</Description><SnippetTypes><SnippetType>Expansion</SnippetType></SnippetTypes><Shortcut>proIPC</Shortcut></Header><Snippet><References /><Imports /><Declarations><Literal Editable="true"><ID>name</ID><Type></Type><ToolTip>Property Name</ToolTip><Default>MyProperty</Default><Function></Function></Literal>        <Literal Editable="true"><ID>type</ID><Type></Type><ToolTip>Property Type</ToolTip><Default>string</Default><Function></Function></Literal></Declarations><Code Language="csharp" Kind="method decl" Delimiter="$"><![CDATA[public $type$ $name$
{get { return _$name$; }set{if (value == _$name$) return;_$name$ = value;OnPropertyChanged(nameof($name$));}
}        
private $type$ _$name$;
]]></Code></Snippet></CodeSnippet></CodeSnippets>

一旦文件存在或更新了,Visual Studio 无需重启,就能立即发现并使用。在相关的 (如 C#) 编辑器中,立马就能看到智能提示中的代码段:

640?wx_fmt=png

它插入对应的模板并允许您编辑在模板中声明的 $expr$ 占位符:

640?wx_fmt=png

这个 C# 代码片段示例, 是 VS 中最常见的语言。如您所见, XML文件中的 <Code> 节点定义模板文本,Shortcut 节点定义触发提示的按键,你可以使用在 <Declaration> 节点中使用类似 $txt$ 占位符来定义参数,同样的占位符在多个地方出现也能同步更改。

对于我来说,最有用和最常用的代码片段时用于插入 HTML 代码,特别是在定义 Bootstrap 结构或其他很难记住语法的自定义控件。我喜欢在浏览文档网站后创建一个对应的片段,这样就很方便使用。用多几次,省下来的时间就赚翻了。花费几分钟设置模板可以节省大量时间去输入重复代码,尤其是您每次都要浪费时间查找相同的 Bootstrap 代码时。?

前缀以及代码片段包

Visual Studio Marketplace中还有许多可用的代码片段,您可以安装使用一整套预设的代码片段。例如,Bootstrap Snippet 包就内置了一堆以 bs- 为前缀的代码片段。

640?wx_fmt=png

即使您自己有专属的代码片段,最好为您的代码片段创建一个前缀,以便您可以在智能提示的海洋中轻松地找到它们。我一般使用 ww- 作为大多数代码片段的前缀。不幸的是,我自己没有很好得遵循这个建议,还是有不少代码片段没有这么做。

构建转换器

因为我在 Visual Studio 大量使用了代码片段,所以我做了一个将 Visual Studio 中的代码片段迁移到 VS Code 中的小工具,同时尽量能迁移到 JetBrains Rider 。

我想可能还有其他人需要用到,所以我把它作为 .NET Global Tool 控制台应用程序发布,以便快速安装:

dotnet tool install dotnet-snippetconverter

您需要.NET Core 2.1 SDK或更高版本才能运行它。

以下示例命令将代码片段从 Visual Studio 迁移到 VS Code,稍后再讨论迁移到 Rider 的事

安装后,您可以使用以下命令快速将所有 Visual Studio 代码片段转换为 VS Code 可以接受的格式。

snippetconverter ~ -r -d 

这将转换最新安装的 Visual Studio 版本(2017,2019等)中的所有代码片段,并在位于%appdata%\Code\User\snippets 路径的 VS Code 的代码文件夹中创建单独的 visualstudio-exported.code-snippets 文件夹。

您还可以导出特定 VS 版本的代码片段:

snippetconverter ~2017 -r -d

或特定文件夹:

snippetconverter "~2017\Visual C#\My Code Snippets" -r -d -o "~\ww-csharp.code-snippets"

其中输入和输出文件夹选项中的路径都是可选的,示例中的~ 是物理片段路径的占位符,会指向 Visual Studio(%Documents%\Visual Studio <year>\Code Snippets)和 VS Code(%appdata%\Code\User\Snippets\)中存放代码片段的基本位置,因此您不必每次都指定完整路径。您高兴的话,也可以使用合格的全路径。

最后,您还可以导出单个文件:

snippetconverter "~2017\Visual C#\My Code Snippets\proIPC.snippet" -d -o "~\ww-csharp.code-snippets"

如果 VS Code 中已存在该代码片段,则会覆盖更新,所以每次重新运行都会更新对应的代码片段。

运行迁移工具后,在VS Code 中通过前缀或者快捷方式就可以立即使用:

640?wx_fmt=png

在 Visual Studio 中多个占位符输入也是支持的:

640?wx_fmt=png

同步代码片段

目前只支持从Visual Studio 单向 迁移到到 VS Code。这意味着如果要保持 Visual Studio 和 VS Code 之间的代码段同步,最好是在 Visual Studio 中创建代码片段,然后通过此工具将它们迁移到 VS Code。

VS Code 中的代码片段

我之前讨论过 Visual Studio Snippets 的代码片段格式,现在让我们看看 VS Code 中又是什么样的。

  • 存放在 %AppData\Code\User\snippets

  • 使用 JSON 格式化

  • 命名为 lang.json

  • 或者是 <name>.code-snippet 的命名格式

  • 可以包含一个或者多个代码片段

转换器之所以导出为 .code-snippet 文件格式,是因为使用 lang.json 很容易造成命名冲突。如果默认的 visualstudio-export.code-snippets 不能使用,则使用 -o 来指定输出文件。

VS Code 代码片段文件是 JSON,它们看起来像:

{  "proipc": {    "prefix": "proipc",    "scope": "csharp",    "body": [      "public ${2:string} ${1:MyProperty}",      "{",      "    get { return _${1:MyProperty}; }",      "    set",      "    {",      "        if (value == _${1:MyProperty}) return;",      "        _${1:MyProperty} = value;",      "        OnPropertyChanged(nameof(${1:MyProperty}));",      "    }",      "}        ",      "private ${2:string} _${1:MyProperty};",      ""],    "description": "Control Property with Attributes"},  "commandbase-object-declaration": {    "prefix": "commandbase",    "scope": "csharp",    "body": [      "        public CommandBase ${1:CommandName}Command { get; set;  }",      "",      "        void Command_${1:CommandName}()",      "        {",      "            ${1:CommandName}Command = new CommandBase((parameter, command) =>",      "            {",      "              $0",      "            }, (p, c) => true);",      "        }",      ""],    "description": "Create a CommandBase implementation and declaration"} 
}

VS Code 的代码模板在概念上更简单,只有模板,前缀和范围,以及使用字符串插值和约定来确定如何定义占位符。当然还有其他字段可以填充,但大多数值是可选的,对于从 Visual Studio 转换过来的代码片段用不到。

您可以在此处找到Visual Studio代码段模板文档:

  • VS Code 代码段模板文档

但是实际上,自己手动创建模板,定义 JSON中的 body 属性还是有难度的,因为字符串可能只是一个字符串数组(yuk),也可能是一个可以输入的类型。好在只是从 Visual Studio 代码片段转换,还是很容易生成对应的模板...

咦?导出到 Rider

转换器某种程度上可以适配到 Rider,但功能有限。因为Rider 使用令人抓狂的模式来存储代码片段,使用 GUID 来标识的 XML 文件。

%USERPROFILE%\.Rider2018.2\config\resharper-host\GlobalSettingsStorage.DotSettings

让我们看看几个导出的模板效果:

<root><s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=720E28E0ECFD4CA0B80F10DC82149BD4/Reformat/@EntryValue">True</s:Boolean><s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=720E28E0ECFD4CA0B80F10DC82149BD4/Shortcut/@EntryValue">proipc</s:String><s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=720E28E0ECFD4CA0B80F10DC82149BD4/ShortenQualifiedReferences/@EntryValue">True</s:Boolean><s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=720E28E0ECFD4CA0B80F10DC82149BD4/Scope/=C3001E7C0DA78E4487072B7E050D86C5/@KeyIndexDefined">True</s:Boolean><s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=720E28E0ECFD4CA0B80F10DC82149BD4/Scope/=C3001E7C0DA78E4487072B7E050D86C5/Type/@EntryValue">InCSharpFile</s:String><s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=720E28E0ECFD4CA0B80F10DC82149BD4/Text/@EntryValue">public $type$ $name$
{get { return _$name$; }set{if (value == _$name$) return;_$name$ = value;OnPropertyChanged(nameof($name$));}
}        
private $type$ _$name$;    </s:String><s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=720E28E0ECFD4CA0B80F10DC82149BD4/Field/=name/@KeyIndexDefined">True</s:Boolean><s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=720E28E0ECFD4CA0B80F10DC82149BD4/Field/=name/Expression/@EntryValue">complete()</s:String><s:Int64 x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=720E28E0ECFD4CA0B80F10DC82149BD4/Field/=name/Order/@EntryValue">0</s:Int64><s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=720E28E0ECFD4CA0B80F10DC82149BD4/Field/=type/@KeyIndexDefined">True</s:Boolean><s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=720E28E0ECFD4CA0B80F10DC82149BD4/Field/=type/Expression/@EntryValue">complete()</s:String><s:Int64 x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=720E28E0ECFD4CA0B80F10DC82149BD4/Field/=type/Order/@EntryValue">1</s:Int64><s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=E88A906D39C741C0A3B8095C5063DADE/@KeyIndexDefined">True</s:Boolean><s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=E88A906D39C741C0A3B8095C5063DADE/Applicability/=Live/@EntryIndexedValue">True</s:Boolean><s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=E88A906D39C741C0A3B8095C5063DADE/Reformat/@EntryValue">True</s:Boolean><s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=E88A906D39C741C0A3B8095C5063DADE/Shortcut/@EntryValue">seterror</s:String><s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=E88A906D39C741C0A3B8095C5063DADE/ShortenQualifiedReferences/@EntryValue">True</s:Boolean><s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=E88A906D39C741C0A3B8095C5063DADE/Scope/=C3001E7C0DA78E4487072B7E050D86C5/@KeyIndexDefined">True</s:Boolean><s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=E88A906D39C741C0A3B8095C5063DADE/Scope/=C3001E7C0DA78E4487072B7E050D86C5/Type/@EntryValue">InCSharpFile</s:String><s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=E88A906D39C741C0A3B8095C5063DADE/Text/@EntryValue">      public string ErrorMessage {get; set; }protected void SetError(){this.SetError("CLEAR");}protected void SetError(string message){if (message == null || message=="CLEAR"){this.ErrorMessage = string.Empty;return;}this.ErrorMessage += message;}protected void SetError(Exception ex, bool checkInner = false){if (ex == null)this.ErrorMessage = string.Empty;Exception e = ex;if (checkInner)e = e.GetBaseException();ErrorMessage = e.Message;}    </s:String><s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=E88A906D39C741C0A3B8095C5063DADE/Field/=busObject/@KeyIndexDefined">True</s:Boolean><s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=E88A906D39C741C0A3B8095C5063DADE/Field/=busObject/Expression/@EntryValue">complete()</s:String><s:Int64 x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=E88A906D39C741C0A3B8095C5063DADE/Field/=busObject/Order/@EntryValue">0</s:Int64><s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=E88A906D39C741C0A3B8095C5063DADE/Field/=NewLiteral/@KeyIndexDefined">True</s:Boolean><s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=E88A906D39C741C0A3B8095C5063DADE/Field/=NewLiteral/Expression/@EntryValue">complete()</s:String><s:Int64 x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=E88A906D39C741C0A3B8095C5063DADE/Field/=NewLiteral/Order/@EntryValue">1</s:Int64></root>    

使用这种疯狂的格式,无法分辨一组代码片段的开始和结束的位置。每个代码片段都有多个 Key,加上 GUID 标识,这使得匹配现有的代码段来判断是否存在的目的几乎不可能实现。

据我所知,没有找到任何相关键值配置的文档,也没有如何存储的文档。很有可能存在其他存储选项,但看起来 Rider 并没有为代码片段设置编辑功能。如果您有更好的开发人员文档,请发表评论。

出于这个原因,Rider 导入是一次性的,如果您导出两次相同的片段,它们就会翻倍。

为了测试,我在 Rider 的导出文件中添加了一个标记键。然后,在我导入相同的代码片段时,我会删除了之前添加的代码片段。很简陋,也只是测试阶段。如果相关的配置发生了变化,则可能会失效。

此格式仅适用于 Rider 支持的 .NET 特定代码类型:.NET Languages,Razor 和包含 HTML 模板的 WebForms。其他格式( JavaScript、HTML 、CSS)则使用完全独立的格式,我没有精力在实现相关的功能。对于 Rider,我主要关心的是 C# 和 HTML 模板,能正常运行就好了。

只需导出特定文件夹,如 C# 文件夹或 HTML 代码段,而不是批量导出整个代码片段文件夹。

SnippetConverter "~2017\Visual C#\My Code Snippets" -m vs-rider -d
SnippetConverter "~2017\Code Snippets\Visual Web Developer\My HTML Snippets" -m vs-rider -d

摘要

正如我前面提到的,所有这些都是非常简陋,但对于将我全部的代码片段从 Visual Studio 导出到 Visual Studio Code 是完全够用的。对于 Rider, C# 和 HTML 代码片段导出也可以做到,但是其他类型(如 JavaScript、CSS)会出现异常。我只是当作个人工具,如果哪天有足够的兴趣的话,我会接着完善,但是很大程度是需要另外搞一个完全独立的转换器。

我没有测试所有的 Visual Studio 支持的文件类型,即使是VS 内置的代码片段也可能存在某些问题。保险一点,请不要批量导出所有代码段,而是单独导出每种类型的代码片段。

我还是强烈建议使用前缀,因为可以更容易地找到你的代码片段,并保持它们不受影响。

现在这个工具对于我来说已经足够了,但是我很想知道我是否是少数几个投身到代码片段转换的人之一?

相关资源

  • Visual Studio SnippetConverter on GitHub

  • dotnet-snippetconverter .NET Global Tool

原文地址:https://www.cnblogs.com/chenug/p/10289866.html


.NET社区新闻,深度好文,欢迎访问公众号文章汇总 http://www.csharpkit.com

640?wx_fmt=jpeg


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/317687.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何基于 Kubernetes 构建完整的 DevOps 流水线

前言关于 DevOps 是一个很大的话题&#xff0c;它可能既涉及到公司的技术文化构建&#xff0c;也包括开发者技术能力的支持&#xff0c;这次技术干货分享主要是侧重于技术方面&#xff0c;就是如何用 Kubernetes 来服务好 DevOps 的流水线。本文从 4 个方面介绍&#xff1a;什么…

[SNOI2017]遗失的答案 (FWT)

description 小皮球在计算出答案之后&#xff0c;买了一堆皮肤&#xff0c;他心里很开心&#xff0c;但是一不小心&#xff0c;就忘记自己买了哪些皮肤了。 ||| 万幸的是&#xff0c;他还记得他把所有皮肤按照 1∼N 来编号&#xff0c;他买来的那些皮肤的编号&#xff08;他至…

Abp中使用可视化的日志面板

如果你还不了解LogDashboard请看这里 使用logdashboard查看可视化日志。ABP的相关知识不做介绍如果有需要请阅读ABP官方文档ABP是Net下非常优秀的开发框架,在中国很多的项目都正在使用它。现在我们可以使用LogDashboard增强在使用ABP开发中的查看日志能力。下载ABP模板项目打开…

J - Just Multiplicative Inverse Gym - 102875J

J - Just Multiplicative Inverse Gym - 102875J 题目&#xff1a; 题解&#xff1a; 给定一个x&#xff0c;求出F(1,x)F(2,x)…F(x-1,x) 的和除以&#xff08;x-1&#xff09; F(x,p)题目已经给出 我们观察F()含义&#xff0c;再结合本题含义&#xff0c;本题并不是要求F(x,…

利用Topshelf把.NET Core Generic Host管理的应用程序部署为Windows服务

2019第一篇文章。此文源于前公司在迁移项目到.NET Core的过程中&#xff0c;希望使用Generic Host来管理定时任务程序时&#xff0c;没法部署到Windows服务的问题&#xff0c;而且官方也没给出解决方案&#xff0c;只能关注一下官方issue #809 等他们方解决了。官方文档只提供了…

开源项目商业模式分析(2) - 持续维护的重要性 - Selenium和WatiN

该系列第一篇发布后收到不少反馈&#xff0c;包括&#xff1a;第一篇里说的MonicaHQ不一定盈利没错&#xff0c;但是问题在于绝大多数开源项目商业数据并没有公开&#xff0c;从而无法判断其具体是否盈利。难得MonicaHQ是公开的&#xff0c;所以才用来做这系列文章的开篇。很多…

深入业务成为更好的软件架构师——信息化建设图鉴一二例

软件开发实际上跟英语比较类似&#xff0c;都是一项工具&#xff0c;服务于各行各业。从程序员的个人修养上来讲&#xff0c;一是要研习好软件开发这门技艺&#xff0c;二是要深入到所服务的行业。说到底&#xff0c;软件的终极目标是模拟业务&#xff0c;在此期间常常会有一个…

恭贺微软技术俱乐部苏州站正式成立

今天去苏州微软中国&#xff0c;参加了微软技术俱乐部苏州站的成立大会。大会的历程悉数经历&#xff0c;这会儿仍在为各位大佬、社区领袖的奉献精神所感动。在通过我们公司同事分享的链接报名时就了解到&#xff0c;大会上有苏震巍老师和蒋金楠老师的分享&#xff0c;便笃定了…

[HNOI2013]消毒 (匈牙利最大匹配)

Description 最近在生物实验室工作的小T遇到了大麻烦。 由于实验室最近升级的缘故&#xff0c;他的分格实验皿是一个长方体,其尺寸为abc&#xff0c;a、b、c 均为正整数。为了实验的方便&#xff0c;它被划分为abc个单位立方体区域&#xff0c;每个单位立方体尺寸为111。用(i,…

.NET Core微服务之路:基于Ocelot的API网关实现--http/https协议篇

前言 最近一直在忙公司和私下的兼职&#xff0c;白天十个小时&#xff0c;晚上四个小时&#xff0c;感觉每天都是打了鸡血似的&#xff0c;精神满满的&#xff0c;连自己那已经学打酱油的娃都很少关心&#xff0c;也有很长一段时间没有更新博客了&#xff0c;特别抱歉&#…

[NOI2009] 变换序列 (匈牙利最大匹配)

description … solution 我竟然一眼题&#xff01;&#xff01; 变换后的TTT数组是[0,n)[0,n)[0,n)的排列&#xff0c;变换规则也有&#xff0c;距离DDD也知道 很明显可以求出iii的可能变换对象 这不就是个最大匹配&#xff1f;&#xff1f; 无解就是匹配数量达不到nnn罢了…

使用 WeihanLi.Npoi 操作 CSV

Intro最近发现 csv 文件在很多情况下都在使用&#xff0c;而且经过大致了解&#xff0c;csv 格式简单&#xff0c;相比 excel 文件要小很多&#xff0c;读取也很是方便&#xff0c;而且也很通用&#xff0c;微软的 ml.net 的示例项目 用来训练模型的数据也是使用的 csv 来保存的…

种类问题

几乎所有种类问题都可以转化成两种模型之一 1.直接维护ans数组 2.统计二维数点问题 前置知识 二维静态数点 以y为第一元素&#xff0c;x为第二元素&#xff0c;原点优先级大于查询点&#xff0c;对所有点&#xff08;原点查询点&#xff09;&#xff0c;然后求 for(int i1;…

「LibreOJ Round #11」Misaka Network 与测试 (网络流跑二分图匹配)

description 研究者们想要测试 Misaka Network&#xff0c;于是他们把 Misaka Network 中的所有妹妹们召集到了一起。 现在妹妹们排成了 N行 M 列&#xff0c;有的位置没有人。现在研究者们给每一个个体的超能力进行了评定&#xff0c;一共有三个能力等级&#xff1a;Level 1 …

YbtOJ-相似子串【SA,RMQ,二分】

正题 题目大意 给出一个长度为nnn的字符串&#xff0c;两个串相似当且仅当可以通过每种字符置换使得它们相同。 qqq次询问这个字符串所有子串中和这个串中sl,rs_{l,r}sl,r​子串有多少个相似的。 1≤n≤105,1≤q≤51051\leq n\leq 10^5,1\leq q\leq 5\times 10^51≤n≤105,1≤…

程序员修仙之路--把用户访问记录优化到极致

点击上方蓝色字体&#xff0c;关注我们菜菜呀&#xff0c;前几天做的用户空间&#xff0c;用户反映有时候比较慢呀CEO,CTO,CFO于一身的CXO是吗&#xff1f;菜菜我把你拉进用户反馈群&#xff0c;你解决一下呀CEO,CTO,CFO于一身的CXO&#xff08;完了&#xff0c;以后没清净时候…

[国家集训队]航班安排 (最大费用最大流)

description 神犇航空有K架飞机&#xff0c;为了简化问题&#xff0c;我们认为每架飞机都是相同的。神犇航空的世界中有N个机场&#xff0c;以0…N-1编号&#xff0c;其中0号为基地机场&#xff0c;每天0时刻起飞机才可以从该机场起飞&#xff0c;并不晚于T时刻回到该机场。一…

新数据革命:开源图形化数据引擎Hawk5发布

Hawk是一款开源图形化的爬虫和数据清洗工具&#xff0c;GitHub Star超过2k&#xff0c;前几代版本介绍如下&#xff1a;Hawk3: 终于等到你: 图形化开源爬虫Hawk 3发布!Hawk2: 120项优化: 超级爬虫Hawk 2.0重磅发布&#xff01;Hawk1: 如何从互联网采集海量数据&#xff1f;租房…

[TJOI2018]智力竞赛 (匈牙利)

description 题目描述 小豆报名参加智力竞赛&#xff0c;他带上了 n个好朋友作为亲友团一块来参加比赛。 比赛规则如下&#xff1a;一共有 m道题目&#xff0c;每个人都有 1 次答题机会&#xff0c;每次答题为选择一道题目回答&#xff0c;在回答正确后&#xff0c;可以从这个…

ASP.NET Core如何在ActionFilterAttribute里做依赖注入

点击蓝字关注我在ASP.NET Core里&#xff0c;我们可以使用构造函数注入很方便地对Controller&#xff0c;ViewComponent等部件做依赖注入。但是如何给过滤器ActionFilterAttribute也用上构造函数注入呢&#xff1f;问题我的博客系统里有个用来删除订阅文件缓存的ActionFilter&a…