写过任何桌面应用,尤其是WinForm的朋友们应该见过,Main函数上放置了一个[STAThread]的Attribute。 而且几乎所有的桌面应用框架,都是由同一个UI线程来执行渲染操作的,这表现为从其他线程修改控件的值就会抛异常:
1await Task.Run(() => control.Content = ""); // throws exception
大家一定都能猜出STA和UI线程一定有千丝万缕的联系,事实也的确如此(WinUI 3也是一个STA的框架)。
如何在其他线程修改UI
不论在什么框架中,只要使用了异步,就一定会有这种需求,因为异步获取到的数据就是为了显示的。
WPF中,若要从别的线程修改控件的属性,你需要使用Dispatcher(DispatcherPriority是可选参数):
1control.Dispatcher.Invoke(DispatcherPriority.Normal, () => control.Content = "");
而WinUI 3中也有类似的操作,只是API名称有些变化(注意应使 ...
前言
最近遇到了一个比较奇怪的需求:我的电脑只能访问到局域网内的服务器A,而服务器A可以访问到服务器B国内网络, 服务器B可以访问到国外网络。
当电脑简单地使用服务器A进行代理的时候,就可以变成正常的家庭网络了(只能访问国内网络); 但我此时需要使用服务器B进行代理以访问github的时候,发现系统代理已经被设置了。 这时我想起用Proxifier解决这个问题。
工具介绍
Proxifier
Proxifier通过对R0层劫持过滤驱动实现对所有软件都几乎透明的代理。
使用这个软件的原因除了可以更方便地处理路由之外,还是为了让不走系统代理的应用也可以走代理。 此外,实现嵌套代理也离不开它。
V2Ray (Core)
V2Ray是当前主流的代理软件之一,功能很强大、灵活。
但缺点是现在常见的V2Ray可视化客户端(如V2RayN)都无法实现V2Ray的全部功能, 为了更好地指定路由,我使用V2Ray Core(以下简称V2Ray)来实现需要的功能。
梳理流程
当访问国内网络时,我们只需要像普通代理一样使用服务器A:
主机 --> 服务器A --> 国内网站
当访问国外网络时, ...
效果
显示大量弹幕、允许重叠、弹幕字号允许不同
约定
为了更好地进行讨论,我们先声明一些共识:
弹幕会从屏幕右边缘发射,并向左滚动
弹幕出现位置应该尽量靠上
几条弹幕之间应该尽量不要重叠,如果要重叠也要尽量重叠长度少一些
此外本文会创造/使用一些概念:
弹幕:计算的对象实体,有以下成员:
发射时间:这个实际上决定了弹幕的x坐标
坐标:只有y坐标,是算法最后计算出应该出现的位置
宽度:根据弹幕内容计算出的宽度
高度:由弹幕的字号决定
屏幕右边缘:由于弹幕是从右边出现的,所以右边缘和屏幕宽度都很重要
屏幕宽度:由窗口大小决定
位置(room),可以放置弹幕的空位,由于只需要关注屏幕右边缘线上的空位,所以位置实际上是一个一维变量,并且屏幕边缘上所有的位置合起来是一个一维数组,有以下成员:
高度:位置的高度
坐标:位置的坐标,实际上不是一个字段,而是由前面所有的位置高度综合算出的
上条弹幕:这个位置最近发射的弹幕
停留时间:弹幕在屏幕上停留的时间
流程
如图中的弹幕情况。红色新弹幕发射时,应该插在第几行呢?
---
displayMode: compact
---
g ...
WinUI
未读前言
在写WinUI 3或UWP项目的时候,总觉得resw文件冗长可读性又差,希望要是能用json就好了。 我在测试MakePri.exe1的时候意外发现了它支持一种叫resjson的文件,可以用以取代resw。
本文假定读者以有resw2的使用基础。
priconfig.xml部分内容:
12<indexer-config type="resw" convertDotsToSlashes="true" initialPath=""/><indexer-config type="resjson" initialPath=""/>
对比
resjson虽然没有可视化编辑器,但它可读性十分高,可以直接用文本编辑器编辑。 相对而言resw文本可读性很低,几乎只能用可视化编辑器编辑。
resjson不支持注释,但其实注释除了可视化编辑器内其他地方都用不到,所以并没有什么用处。
另外用于x:Uid的属性写法,如TextBox.Text,在resjson中须将点换成斜杠,即T ...
前言
写分析器(源生成器)时经常需要引用别的包,但直接引用是无法运行的。 下面我们分不同情况来讨论在分析器项目如何对依赖进行引用。
下图中“包引用”指PackageReference,“项目引用”指ProjectReference。
其中项目引用是通过dll文件传递的,包引用是通过nupkg文件传递的, 最大的区别是项目引用默认不可以传递,而包引用默认可以传递。
flowchart TD
A1--项目引用-->A2
GA--包引用-->A3
GB1--包引用-->B2
GB1-.包引用.->B3
GB2--包引用-->B3
C1--项目引用-->C2
subgraph G1[单NuGet包项目]
subgraph GA[NuGet包]
direction TB
A1[分析器项目]
A2[NuGet包主项目]
end
A3[用户项目]
end
subgrap ...
大致介绍
在写C#程序时经常有与本地代码(C/C++)代码交互的需求。微软提供了许多种方式供我们选择, 最常用的有以下三种(A->B指A可以引用B):
flowchart LR
A--P/Invoke-->B
A<--C++ Interop-->C
A<--COM Interop-->D
D<-->C-->B
subgraph 托管环境
A[.NET应用程序]
end
subgraph 非托管环境
B[C 库函数]
C[C++ 类库]
D[COM组件]
end
P/Invoke (Platform Invoke):平台调用,是一种用于和非托管函数进行交互的技术, 在调用Windows API的时候有大量的运用。特点是无需编写兼容层代码即可使用。
C++ Interop:托管C++,使用C++/CLI (Common Language Infrastructure)语言, 特点是可以将 ...
前言
微软的许多XAML框架,如WPF、UWP、WinUI 3等,在DataTemplate下都会遇到变量隐藏(Variable shadowing)的问题。为了访问外部实例成员,经常需要写很多曲折的代码,但也没有办法。本文也无法解决这个问题,但记录了我知道的方法,以便在各种情况使用,争取将可读性的影响降到最低。
问题再现
按照需求创建了一个Page:
1234567891011121314151617public sealed partial class SamplePage : Page{ public string OuterMember { get; set; } = "OuterMember"; public SamplePage() { ViewModels = new ViewModel[] { new("a"), new("b"), new("c"), }; InitializeComp ...
前言
一天,数学家觉得自己已受够了数学,于是他跑到消防队去宣布他想当消防员。
消防队长说:「您看上去不错,可是我得先给您一个测试。」
消防队长带数学家到消防队后院小巷,巷子里有一个货栈,一只消防栓和一卷软管。
消防队长问:「假设货栈起火,您怎么办?」
数学家回答:「我把消防栓接到软管上, 打开水龙头,把火浇灭。」
消防队长说:「完全正确。最后一个问题:假设您走进小巷,而货栈没有起火,您怎么办?」 数学家疑惑地思索了半天,终于答道:「我就把货栈点着。」
消防队长大叫起来:「什么?太可怕了,您为什么要把货栈点着?」
数学家回答:「这样我就把问题化简为一个我已经解决过的问题了。」
这个笑话虽然是开玩笑,但也反映了数学思维的解题方法。在学到谓词逻辑的时候,我就在想要是能把谓词转化为命题逻辑词,用与、或、非来判断,该有多方便。
定义
首先给出所有逻辑词的递归定义(只用与、或、非表达)。这些定义书上都有,也易证。
名词
符号
定义
否定
非
合取
与
析取
或
条件
同或
与非
或非
条件否定
异或
任意 ...
原文
在机器学习视频反向传播章节1中:
我们用 来表示误差,则: 。我们利用这个误差值来计算前一层的误差:
。其中 是 形函数的导数,
。而 则是权重导致的误差的和。
问题
看到这道算式时我百思不得其解。为什么凭空会有转置?
在我自己推一遍之后,发现原公式中可能有些不严谨的地方,所以在此阐述我的理解,欢迎大家指正:
前提
对数似然代价函数:
估计函数:
Logistic激活函数:
此外激活函数导数为:
我的理解
flowchart LR
x1--"(Θ<sub>1</sub><sup>(1)</sup>)<sub>1</sub>"-->z12
x1--"(Θ<sub>1</sub><sup>(1)</sup>)<sub>2</sub>"-->z22
x2--"(Θ<sub>2</sub><sup>(1)</sup>)<su ...
前言
源生成器(增量生成器)由于它特殊的定位,关于它的调试十分困难。在这里分享一些调试它的经验。
另外经常有写类库,然后提供可以生成代码的Attribute给用户的需求,此时需要用到传递引用的知识点。
调试源生成器
源生成器执行时间
源生成器项目和普通的项目不同。
普通的会在你按下运行或调试后才会运行;而源生成器会在两种情况下运行:
重新生成解决方案或该项目时候运行,运行后会生成dll文件。在下一次启动VS的时候,会连着dll一起读取,所以可能会有VS找不到生成的文件导致报错,但可以正常运行的问题,重启VS即可。
在生成项目后第二次及以后打开项目时,每次对代码进行更改都会重新运行源生成器的dll,并实时将生成的代码加入到项目中。所以源生成器的执行效率很大程度关乎用户的编程手感。
以下程序段默认引用命名空间:
1using System.Diagnostics;
启动调试器
在源生成器项目中,直接在Visual Studio用鼠标点击行号左边打的(红色圆形的)断点是没有用的,需要添加一条Debugger.Launch();,表示启动了调试器。
如果程序运行到这条语句时,会弹出一个窗口,选 ...