在我参与的其中一个项目中,有大量使用WeakAction
.这是一个类,它允许保持对动作实例的引用,而不会导致其目标不被垃圾回收.它的工作方式很简单,它在构造函数上执行操作,并保持对操作的目标和方法的弱引用,但丢弃对操作本身的引用.当执行操作时,它会检查目标是否仍然存活,如果是,则调用目标上的方法.
除了一个案例 - 当一个闭包中实例化动作时,一切都很好.考虑以下示例:
public class A
{
WeakAction action = null;
private void _register(string msg)
{
action = new WeakAction(() =>
{
MessageBox.Show(msg);
}
}
}
Run Code Online (Sandbox Code Playgroud)
由于lambda表达式使用msg
局部变量,因此C#编译器会自动生成一个嵌套类来保存所有闭包变量.该操作的目标是嵌套类的实例而不是A的实例.WeakAction
一旦构造函数完成,就不会引用传递给构造函数的操作,因此垃圾收集器可以立即处理它.稍后,如果WeakAction
执行,它将无法工作,因为目标不再存在,即使原始实例A
是活着的.
现在我无法改变WeakAction
被调用的方式(因为它被广泛使用),但我可以改变它的实现.我正在考虑尝试找到一种方法来访问A的实例,并强制嵌套类的实例保持活着,而A的实例仍然存活,但我不知道该怎么做.
关于什么A
与任何事情有关,有很多问题,改变方式的建议A
会产生一个弱行动(我们做不到),所以这里有一个澄清:
类A
的实例希望类的实例B
在发生某些事件时通知它,因此它使用Action
对象提供回调.A
不知道B
使用弱动作,它只是提供一个Action
作为回调.这事实上B
使用WeakAction
是一个实现细节不暴露.B
需要存储此操作并在需要时使用它.但是B
可能活得更久A
,并且持有对正常Action的强引用(它本身拥有A
生成它的实例的强引用)导致A
永远不会被垃圾收集.如果A
是不再存活的项列表的一部分,我们期望A
被垃圾收集,并且由于B
Action 的引用,它本身指向 …
我创建了一个用户控件,我将其用于不同的目的.我已经定义了一个包含UIElement的依赖属性,我将其作为用户控件中某个区域的内容呈现.
当我使用这个控件时,我注意到了.并为content属性中的元素指定名称,它们在运行时始终显示为null.
MyContainer.xaml:
<UserControl x:Class="BSSApp.UI.Tests.MyContainer" x:Name="userControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<Border x:Name="LayoutRoot" Background="Green" CornerRadius="20" BorderBrush="#005500" BorderThickness="10">
<ContentPresenter Content="{Binding ElementName=userControl, Path=MyContent}" Margin="20"/>
</Border>
</UserControl>
Run Code Online (Sandbox Code Playgroud)
MyContainer.xaml.cs
namespace BSSApp.UI.Tests
{
public partial class MyContainer : UserControl
{
public static readonly DependencyProperty MyContentProperty =
DependencyProperty.Register("MyContent",
typeof(UIElement),
typeof(MyContainer),
new PropertyMetadata(new Grid()));
public UIElement MyContent
{
get
{
return (UIElement)GetValue(MyContentProperty);
}
set
{
SetValue(MyContentProperty, value);
}
}
public MyContainer()
{
InitializeComponent();
}
}
}
Run Code Online (Sandbox Code Playgroud)
而使用类:UserControlContentBug.xaml:
<UserControl x:Class="BSSApp.UI.Tests.UserControlContentBug"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:t="clr-namespace:BSSApp.UI.Tests"
mc:Ignorable="d" …
Run Code Online (Sandbox Code Playgroud) 当在工作线程上调用的方法需要在UI线程上运行代码并在执行其他操作之前等待它完成时,可以这样做:
public int RunOnUi(Func<int> f)
{
int res = Application.Current.Dispatcher.Invoke(f);
return res;
}
Run Code Online (Sandbox Code Playgroud)
但是,如果我想用任务做什么呢?有没有办法让RunOnUi方法创建一个在UI上启动并返回它的任务,以便调用者(在工作线程上运行)可以等待它?符合以下签名的东西:public Task<int> StartOnUi(Func<int> f)
?
一种方法如下:
public Task<int> RunOnUi(Func<int> f)
{
var task = new Task<int>(f);
task.Start(_scheduler);
return task;
}
Run Code Online (Sandbox Code Playgroud)
这里,假设_schduler
持有ui TaskScheduler
.但是我不太习惯于创建"冷"任务并使用start方法来运行它们.这是"推荐"的方式还是有更优雅的方式来做到这一点?
我跟随Tomek Janczuk在silverlight tv上的演示,创建了一个使用WCF Duplex Polling Web服务的聊天程序.客户端订阅服务器,然后服务器向所有连接的客户端发起通知以发布事件.
想法很简单,在客户端上,有一个允许客户端连接的按钮.客户端可以编写消息并将其发布的文本框,以及显示从服务器接收的所有通知的更大文本框.
我连接了3个客户端(在不同的浏览器中 - IE,Firefox和Chrome),它们都运行良好.他们发送消息并顺利接收.当我关闭其中一个浏览器时,问题就出现了.一个客户一出门,其他客户就会卡住.他们停止收到通知.
我猜测服务器中通过所有客户端并向他们发送通知的循环卡在现在丢失的客户端上.我尝试捕获异常并将其从客户端列表中删除(请参阅代码)但它仍然没有帮助.
有任何想法吗?
服务器代码如下:
using System;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.Collections.Generic;
using System.Runtime.Remoting.Channels;
namespace ChatDemo.Web
{
[ServiceContract]
public interface IChatNotification
{
// this will be used as a callback method, therefore it must be one way
[OperationContract(IsOneWay=true)]
void Notify(string message);
[OperationContract(IsOneWay = true)]
void Subscribed();
}
// define this as a callback contract - to allow push
[ServiceContract(Namespace="", CallbackContract=typeof(IChatNotification))]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]
public class ChatService …
Run Code Online (Sandbox Code Playgroud) 我正在编写一个涉及动作的组件,当Action.Target对象是编译器已生成的闭包时,它就要求找到一种使用反射案例进行标识的方法。我正在做一个实验,试图找到一种方法,这个小实验的目的是开发一个带Action的谓词,并返回一个布尔值,该布尔值告诉action目标是否是此类闭包类的实例。
在我的测试案例中,我具有以下创建4种不同类型的动作的方法:
private void _createClosure(int i)
{
ClosureAction = new Action(() =>
{
var j = i;
var k = somenum;
});
}
private void _createLambda()
{
LambdaAction = new Action(() =>
{
this._instanceAction();
});
}
private void _createInstance()
{
InstanceAction = new Action(_instanceAction);
}
private void _createStatic()
{
StaticAction = new Action(_staticAction);
}
private int somenum;
private void _instanceAction()
{
somenum++;
}
private static void _staticAction()
{
}
Run Code Online (Sandbox Code Playgroud)
下表显示了每个操作的属性:
如您所见,LambaAction和ClosureAction在定义方面非常相似,它们都使用lambda,但是闭包一个具有在lambda内部使用的局部函数变量,因此编译器被迫生成闭包类。很明显,第二行,即呈现ClosureAction的行,是唯一具有闭包类型目标的行。静态的一个根本没有目标,而另外两个则使用调用类(Called ActionReferences)作为目标。下表提供了目标反射类型属性的比较:
因此,我们可以看到闭包情况的独特之处在于目标类型不是类型信息,而是嵌套类型。它也是唯一一个私有嵌套,密封且名称包含字符串+ <> c__DisplayClass的名称。现在,尽管我认为这些特征对于任何正常使用情况都是决定性的,但我还是希望定义一个我可以依靠的谓词。我不希望这种机制基于编译器的命名约定或不唯一的属性,因为从技术上讲,用户可以使用相同的命名约定创建一个私有的嵌套密封类……这不太可能,但这并不是100%干净的解决方案。
所以最后-问题是这样的:是否有一种干净的方法来编写谓词,这些谓词实际上是编译器生成的闭包的identify操作?
谢谢
是否可以编写一个 C# 方法来接受具有任意数量的相同类型项的值元组并将它们转换为列表?
编辑 2/6/2019 我接受了提供的答案作为正确答案。我还想提供一个使用基类而不是接口的解决方案,因为我正在尝试编写一个转换运算符,并且不允许从接口进行用户定义的转换。
public static class TupleExtensions
{
public static IEnumerable<object> Enumerate(this ValueType tpl)
{
var ivt = tpl as ITuple;
if (ivt == null) yield break;
for (int i = 0; i < ivt.Length; i++)
{
yield return ivt[i];
}
}
}
Run Code Online (Sandbox Code Playgroud) 我有 2 个事件流: 1. 鼠标拖放事件流(拖动开始...拖动结束...拖动开始...拖动结束) 2.按键事件流('a' ... ' b' .... 'c' .... 'd')
我需要合并到一个仅包含第二个流中的事件(因此仅包含按键)的流中,但它需要过滤掉拖动开始和拖动结束之间发生的所有按键(最后一个除外)。
所以如果来源是这样的:
... Start ............... End .............. Start .............. End
Run Code Online (Sandbox Code Playgroud)
和
...........'a'...'b'...'c'.......'d'...'e'..........'f'....'g'.......
Run Code Online (Sandbox Code Playgroud)
结果应该是这样的:
...........................'c'...'d'...'e'..........................'g'
Run Code Online (Sandbox Code Playgroud)
在 C# 中使用 Rx.net 可以实现类似的功能吗?
WPFDataGrid
有一个属性允许虚拟化与分组一起使用:VirtualizationPanel.IsVirtualizingWhenGrouping="True"
。当我将其设置为 True 时,我注意到当整个数据网格宽度发生变化时,具有星形大小的列不再调整大小。
要重建,请创建一个新的 WPF 应用程序,并在 MainWindow.xaml 中设置以下代码:
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d">
<Grid>
<DataGrid x:Name="grid"
CanUserResizeColumns="False"
AutoGenerateColumns="False"
VirtualizingPanel.IsVirtualizingWhenGrouping="True"
>
<DataGrid.GroupStyle>
<GroupStyle/>
</DataGrid.GroupStyle>
<DataGrid.Columns>
<DataGridTextColumn Width="100" Header="1" Binding="{Binding .}">
</DataGridTextColumn>
<DataGridTextColumn Width="*" Header="2" Binding="{Binding .}">
</DataGridTextColumn>
<DataGridTextColumn Width="100" Header="3" Binding="{Binding .}">
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
Run Code Online (Sandbox Code Playgroud)
MainWindow.xaml.cs 中的以下内容:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var csv = new CollectionViewSource();
csv.Source = new List<string>
{
"John",
"Paul",
"George",
"Ringo",
"John", …
Run Code Online (Sandbox Code Playgroud) c# ×4
silverlight ×2
wpf ×2
collections ×1
datagrid ×1
delegates ×1
grouping ×1
lambda ×1
reflection ×1
rx.net ×1
task ×1
tuples ×1
wcf ×1