我完全意识到我提出的建议并不遵循.NET准则,因此,仅凭这个原因可能是一个糟糕的想法.但是,我想从两个可能的角度考虑这个问题:
(1)我是否应考虑将此用于我自己的开发工作,100%用于内部目的.
(2)这是框架设计者可以考虑改变或更新的概念吗?
我正在考虑使用一个使用强类型"发送者"的事件签名,而不是将其键入"对象",这是当前的.NET设计模式.也就是说,而不是使用如下所示的标准事件签名:
class Publisher
{
public event EventHandler<PublisherEventArgs> SomeEvent;
}
Run Code Online (Sandbox Code Playgroud)
我正在考虑使用一个使用强类型'sender'参数的事件签名,如下所示:
首先,定义一个"StrongTypedEventHandler":
[SerializableAttribute]
public delegate void StrongTypedEventHandler<TSender, TEventArgs>(
TSender sender,
TEventArgs e
)
where TEventArgs : EventArgs;
Run Code Online (Sandbox Code Playgroud)
这与Action <TSender,TEventArgs>并没有什么不同,但通过使用StrongTypedEventHandler,我们强制执行TEventArgs派生System.EventArgs.
接下来,作为示例,我们可以在发布类中使用StrongTypedEventHandler,如下所示:
class Publisher
{
public event StrongTypedEventHandler<Publisher, PublisherEventArgs> SomeEvent;
protected void OnSomeEvent()
{
if (SomeEvent != null)
{
SomeEvent(this, new PublisherEventArgs(...));
}
}
}
Run Code Online (Sandbox Code Playgroud)
上述安排将使订阅者能够使用不需要强制转换的强类型事件处理程序:
class Subscriber
{
void SomeEventHandler(Publisher sender, PublisherEventArgs e)
{
if (sender.Name == "John Smith")
{
// ...
}
}
} …Run Code Online (Sandbox Code Playgroud) 在回答这篇文章的同时踢了一些小结构,我意外地遇到了以下情况:
使用int字段的以下结构是完全合法的:
struct MyStruct
{
public MyStruct ( int size )
{
this.Size = size; // <-- Legal assignment.
}
public int Size;
}
Run Code Online (Sandbox Code Playgroud)
但是,使用自动属性的以下结构无法编译:
struct MyStruct
{
public MyStruct ( int size )
{
this.Size = size; // <-- Compile-Time Error!
}
public int Size{get; set;}
}
Run Code Online (Sandbox Code Playgroud)
返回的错误是"在将所有字段分配给"之前,不能使用'this'对象.我知道这是结构的标准过程:任何属性的支持字段必须直接从结构的构造函数中指定(而不是通过属性的set访问器).
解决方案是使用显式支持字段:
struct MyStruct
{
public MyStruct(int size)
{
_size = size;
}
private int _size;
public int Size
{
get { return _size; }
set { _size = value; …Run Code Online (Sandbox Code Playgroud) 在尝试在Visual Studio Professonal 2008的测试功能中创建初始的失败单元测试Assert.ReferenceEquals()时,当对象实例不等于空引用时,我似乎无法正确地失败.请注意,object.ReferenceEquals()正确返回false此相同的比较.
这是我的班级代码:
public static class Project
{
public static object TheObject { get; set; }
public static void Startup(object theObject)
{
// ToDo: Project.Startup(): Test.
// ToDo: Project.Startup(): Implement.
}
}
Run Code Online (Sandbox Code Playgroud)
然后这是我的测试类的关键方面:
[TestClass()]
public class ProjectTest
{
[TestMethod()]
public void StartupTest()
{
object obj = "hello";
Project.Startup(obj);
Assert.ReferenceEquals(obj, Project.TheObject); // Test Passes!?!
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,该static void Startup(object)方法为空,因此static object TheObject永远不会设置该属性null.所以,显然,Assert.ReferenceEquals(obj, Project.TheObject)应该失败,但不知何故,这个测试通过了.
注意改变 …
我的问题与此问题有些相关:显式实现的接口和通用约束.
然而,我的问题是编译器如何启用通用约束来消除对显式实现接口的值类型进行装箱的需要.
我想我的问题归结为两部分:
幕后CLR实现发生了什么,需要在访问显式实现的接口成员时将值类型装箱,并且
删除此要求的通用约束会发生什么?
一些示例代码:
internal struct TestStruct : IEquatable<TestStruct>
{
bool IEquatable<TestStruct>.Equals(TestStruct other)
{
return true;
}
}
internal class TesterClass
{
// Methods
public static bool AreEqual<T>(T arg1, T arg2) where T: IEquatable<T>
{
return arg1.Equals(arg2);
}
public static void Run()
{
TestStruct t1 = new TestStruct();
TestStruct t2 = new TestStruct();
Debug.Assert(((IEquatable<TestStruct>) t1).Equals(t2));
Debug.Assert(AreEqual<TestStruct>(t1, t2));
}
}
Run Code Online (Sandbox Code Playgroud)
由此产生的IL:
.class private sequential ansi sealed beforefieldinit TestStruct
extends [mscorlib]System.ValueType
implements [mscorlib]System.IEquatable`1<valuetype TestStruct>
{
.method …Run Code Online (Sandbox Code Playgroud) 我有一个当前定义没有事件参数的事件.也就是说,它发送的EventArgs是EventArgs.Empty.
在这种情况下,最简单的方法是将我的事件处理程序声明为:
EventHandler<System.EventArgs> MyCustomEvent;
Run Code Online (Sandbox Code Playgroud)
我不打算在此事件中添加任何事件参数,但是将来可能需要更改任何代码.
因此,我倾向于让我的所有事件始终创建一个空事件args类型System.EventArgs,即使当前不需要事件args也是如此.像这样的东西:
public class MyCustomEventArgs : EventArgs
{
}
Run Code Online (Sandbox Code Playgroud)
然后我的事件定义变为以下内容:
EventHandler<MyCustomEventArgs> MyCustomEvent;
Run Code Online (Sandbox Code Playgroud)
所以我的问题是:定义自己的更好MyCustomEventArgs,即使它没有添加任何东西,除了继承System.EventArgs,以便将来可以更容易地添加事件参数?或者将事件明确定义为返回更好System.EventArgs,以便用户更清楚没有额外的事件参数?
我倾向于为我的所有事件创建自定义事件参数,即使事件参数为空.但我想知道是否有人认为让事件参数为空的用户更清楚会更好吗?
非常感谢提前,
麦克风
我想知道在Visual Studio 2008中调试时是否有办法完全锁定我的代码.代码文件在作为64位应用程序运行时自动锁定,我更喜欢这些; 但是,我的大部分编码都是为Excel创建加载项,即32位.结果是,即使我以'AnyCPU'为目标,VS主机也知道它在32位进程内运行,因此,当代码在Visual Studio中托管时,源代码未被锁定.
我可以通过转到"工具">"选项">"调试">"编辑并继续",然后取消选中"启用编辑并继续"复选框来关闭"编辑并继续".但是,这并不能完全锁定代码.这样可以防止代码中的任何编辑在当前运行中执行,但它不会阻止鼠标单击或击键实际更改代码.
同样,当使用64位应用程序时,这不会发生 - 代码完全被锁定.我非常希望代码完全锁定至少有几个原因:
我可以在调试时不小心碰到一个键或类似物,我绝对不想这样做.这很罕见,但这是一个问题.
我的许多自动化测试通过SendKeys驱动用户界面.但是,当使用调试器逐步完成这样的测试时,我有时会忘记某些方面涉及SendKeys,这意味着击键最终会被发送到Visual Studio IDE而不是Excel.
在上面的问题#2中,单元测试失败,这很好 - 我的不好 - 但是将所有击键发送到代码模块并破坏我的代码是完全不可接受的.
有人有任何想法吗?在针对32位CPU编译时,可以在Visual Studio中运行托管时完全锁定代码吗?
关于这个问题的一些相关帖子,但没有一个直接解决这个问题:
在此先感谢任何帮助或想法......
麦克风
我根据Phil Wilson 的特殊文章构建和部署.NET COM程序集创建了一个暴露给COM的.NET程序集.
在.NET程序集正确注册COM的意义上,一切正常,编译的COM代码可以毫无问题地调用它.
唯一奇怪的是,在使用VB 6.0或VBA时,针对暴露于COM的.NET程序集进行开发需要程序员"浏览"到相关.tlb文件的确切文件位置,之后一切正常.也就是说,类库不会直接显示在"引用"对话框中,因此您必须浏览到文件位置.
同样,COM Interop方面确实可以100%工作; 但是,我认为必须有一些设置可以使库直接在VB 6.0和VBA的References对话框中可见.
有谁知道这个设置是什么?或者只是通过注册才能自动发生这种情况?
非常感谢任何建议......
麦克风
编辑/更新
要回答jpoh关于我是否正在使用/ codebase开关的问题,我使用的是.msi安装包,而不是显式使用RegAsm.正确地注册了程序集,可以看出,在HKCR\CLSID {myGUID}\InprocServer32中,'CodeBase'键正确保存了程序集的完整路径.编译的COM组件对这个dll执行就好了,只有在使用VB 6.0或VBA进行开发时它们才会出现在引用对话框中.因此,我需要"浏览"到正确的文件位置,之后它可以100%正常工作.
更新#2
经过进一步研究,似乎虽然类GUID正在正确注册,但我的.tlb文件尚未注册.我不知道为什么不.注册.tlb文件应该为我的类基于HKCR\Interface {myInterfaceGUID}的接口放置一些注册表项,但是这没有发生.奇怪的是,这种缺乏注册似乎并没有影响dll的功能,除了它在VB6和VBA的引用对话框中的可发现性.
安装项目中我的.tlb文件的属性似乎是正确的:'PackageAs'属性设置为'vsdpaDefault','Register'属性设置为'vsdrfCOM'.我很困惑为什么这不会成功安装在目标机器上.
更新#3
好吧,事实证明安装项目没有成功构建......尽管它报告了"构建成功".
实际上有一个构建警告(令人惊讶的是,警告,而不是错误)被报告为"无法为文件名创建注册信息'DotNetLibrary3.tlb'".由于这是一个警告,而不是错误,编译声明"Build Succeeded"并且错误列表没有打开.
对此进行跟踪,当Vista是您的开发机器时,尝试创建安装项目时,这似乎是一个问题,如下所述:
这里描述了一些手动修复:
我还没有尝试修复,但明天我会报告,如果这解决了.
更新#4
这没有那么好用......似乎运行在该文章中建议的RegCap.exe在Vista上运行时不起作用.由于RegCap实际上是由创建项目本身在创建.msi时运行的,因此这并不奇怪.简而言之,安装项目几乎肯定会失败,因为它调用的RegCap命令失败了...所以直接调用RegCap是没有用的.
最重要的是,尝试在Vista上创建安装程序包时,这只是一个错误.或者,它可能是Visual Studio 2008和Vista的组合,我不确定.尝试完全相同的方法是在Windows XP上运行的Visual Studio 2005上创建一个安装项目绝对没有任何问题.
很有可能在Vista和/或Visual Studio 2008上运行正常,但我无法追踪它.对我来说更有效的是在Windows XP上使用Visual Studio 2005生成COM注册要求,然后将它们导入我的Visual Studio 2008安装项目.这些可以通过regasm导出为.REG文件,使用针对dll的/ regfile开关,并使用RegCap(在W'XP!上运行)对.tlb文件.由于我的COM接口不会改变,我只需要做一次.
希望在Vista上运行时Visual Studio 2008中的这个问题会在某些时候得到纠正,但如果没有,希望这篇文章对于在相同情况下发现自己的其他人有一定的价值......
也可以看看:
如何在VB.NET中安装并在Automation Servers列表中注册的用于Excel的COM Server for Excel?
- 迈克
我正在使用Microsoft.Interropt.Excel DLL编写Excel类.我完成了所有功能,但我的析构函数中有错误.我想将所有更改保存到我的文件中,并且我想要释放所有源代码.我希望所有这些都在我的析构函数中.但在我的析构函数中,Excel.ApplicationClass,Workbook和Worksheet对象由异常填充,其中包含消息"已与其基础RCW分离的COM对象无法使用".所以我不能保存任何东西,什么都不关闭因为ı无法访问工作簿或工作表对象.
我不能在Destructor中访问类私有成员吗?
我希望有人可以帮我解决以下问题:
Function Get-FormattedNameValuePair([string] $name, [object] $value)
{
return "$("{0,-24}" -f $name) : $("{0,15:N2}" -f $value)"
}
Write-Output (Get-FormattedNameValuePair -name MyField -value 1234)
Run Code Online (Sandbox Code Playgroud)
以上回报:
MyField : 1234
Run Code Online (Sandbox Code Playgroud)
然而,我期待着:
MyField : 1,234.00
Run Code Online (Sandbox Code Playgroud)
如果我在1234周围添加评估括号,则会正确返回预期结果:
Write-Output (Get-FormattedNameValuePair -name MyField -value (1234))
Run Code Online (Sandbox Code Playgroud)
如果直接调用而不是包含在"Get-FormattedNameValuePair"函数中,则格式化也可以在没有评估括号的情况下工作.
[string] $name = "MyField"
[object] $value = 1234
Write-Output "$("{0,-24}" -f $name) : $("{0,15:N2}" -f $value)"
Run Code Online (Sandbox Code Playgroud)
任何人都能解释上面的行为吗?
我有以下代码部分,旨在计算当前打开的Excel进程数:
Func<int> OpenExcelProcessesCount =
() => System.Diagnostics.Process.GetProcessesByName("Excel")
.Where(p => !p.HasExited)
.Count();
Run Code Online (Sandbox Code Playgroud)
然后我在各个点检索计数,代码如下:
int excelAppCount = OpenExcelProcessesCount();
Run Code Online (Sandbox Code Playgroud)
这段代码已经运行了几个月100%罚款.然后突然,今天,它一直给我一个例外,它读取以下内容:
Run Code Online (Sandbox Code Playgroud)Exception: ApplicationThreadException Message: Access is denied Stack Trace: at System.Diagnostics.ProcessManager.OpenProcess(Int32processId,Int32 access,Boolean throwIfExited)
Run Code Online (Sandbox Code Playgroud)at System.Diagnostics.Process.GetProcessHandle(Int32access,Boolean throwIfExited)
Run Code Online (Sandbox Code Playgroud)at System.Diagnostics.Process.get_HasExited() etc...
基本上,对Process.HasExited(System.Diagnostics.Process.get_HasExited()在上面的堆栈跟踪中显示)的调用失败.错误消息"访问被拒绝"听起来像我没有该进程的管理权限,但是在我当前的用户登录下将创建唯一存在的Excel进程,并且用户始终可以访问自己的进程.我的.NET代码也在完全信任下运行.
最终失败的路线是System.Diagnostics.ProcessManager.OpenProcess(Int32 processId, Int32 access, Boolean throwIfExited).我想知道它是否以'throwIfExited'参数的'true'值传递.如果是这种情况,那么我想我可以Process.HasExited使用try-catch块保护调用,并假设如果失败,HasExited实际上是'true'.但这是一个安全的假设吗?
我不安地做出这样的假设,特别是因为错误信息是"访问被拒绝".有没有人对我如何解决这个问题有任何想法,或者我可能会试图弄清楚发生了什么?
我在Stack Overflow上找到的唯一类似的线程如下:为什么hasExited抛出'System.ComponentModel.Win32Exception'?.那里给出的答案是:
"由于你正在运行runas,你只能在句柄上获得SYNCHRONIZE访问,而不是PROCESS_QUERY_INFORMATION访问,因此GetExitCodeProcess失败,这导致hasEnded抛出Win32异常."
我真的不明白这个答案,不知道这是否适用于我的情况,但我想我应该提一下.如果有人觉得这可能是我所面临的情况,那么如果有人可以尝试为我澄清这个答案,我将非常感激.(我是一名Excel程序员,我没有太多使用流程的经验.)
非常感谢...
更新:
我能说的最好,这是一种一次性的腐败.我所面临的问题开始变得越来越奇怪,因为一组功能完善的单元测试开始在其他"不可能"的位置出现故障.一个简单的重新启动纠正了这个问题和我面临的一切.
我最好的猜测是,我有一些奇怪的腐败.也许ROT已经被破坏了,和/或我有一个挂起的Excel实例,这个实例非常腐败甚至"进程"操作也不一定稳定.没什么结论,但这是我现在能想到的全部.
对于那些花时间回答并帮助我的响应者,我感谢你.
我有一个调用Web服务的excel UDF.UDF代码是一个自动化插件代码,它是一个C#类库,我为它创建了一个安装程序.当我运行设置时,没有对特定插件代码的引用出现在excel的toos - > addins - >自动化插件列表中,并且该功能在公式栏中没有出现.我正在使用VS 2008和Excel 2003.
我在这里错过了什么吗?我应该定义Excel必须从某个东西中选择dll的路径,以便它指向从udf代码生成的dll吗?或者是否有任何安全问题需要处理?客户端计算机的安全级别设置为"完全信任",但插件未显示在列表中.
我在这里错过了什么吗?
c# ×8
.net ×4
events ×2
excel ×2
assertions ×1
automation ×1
boxing ×1
c#-3.0 ×1
casting ×1
com-interop ×1
debugging ×1
deployment ×1
destructor ×1
editing ×1
equals ×1
evaluation ×1
eventargs ×1
finalizer ×1
generics ×1
interface ×1
powershell ×1
process ×1
release ×1
struct ×1
unit-testing ×1
vb.net ×1
vb6 ×1
vba ×1