如何创建暴露给COM的只读属性?

Rub*_*uck 3 c# com properties readonly

我有一个C#类,我想通过COM向VB6公开.问题是这需要为类提供默认构造函数.因此,我必须将setter暴露给客户端,以便可以将这些属性设置为开头.

例如:

[Guid("B1E17DF6-9578-4D24-B578-9C70979E2F05")]
public interface _Class1
{

    [DispId(2)]
    string Message { get; set; }

    [DispId(1)]
    string TestingAMethod();
}

[Guid("197A7A57-E59F-41C9-82C8-A2F051ABA53C")]
[ProgId("Project.Class1")]
[ClassInterface(ClassInterfaceType.None)]
public class Class1 : _Class1
{
    public string Message { get; set; }

    public Class1() { } //default constructor for COM

    public Class1(string message)
    {
        this.Message = message;
    }

    public string TestingAMethod()
    {
        return "Hello World";
    }
}
Run Code Online (Sandbox Code Playgroud)

通常,我会将该财产声明为:

public string Message { get; private set; }
Run Code Online (Sandbox Code Playgroud)

但这显然不起作用,因为COM不能使用接受参数的构造函数.

所以,问题是:

如何确保只使用一次private set或不使用readonly

Rub*_*uck 7

根据代码项目的这篇文章:

如果方法调用失败或业务逻辑验证失败,则.NET组件应引发异常.此异常通常具有分配给它的HRESULT失败以及与之关联的错误描述.CCW从.NET异常中收集错误代码,错误消息等详细信息,并以COM客户端可以使用的形式提供这些详细信息.

强调我的.

那么,我们可以检查是否已设置私有字段.如果是的话,抛出异常.

private string message;
public string Message {
    get { return message; }
    set
    {
        if (message != null)
        {
            throw new ReadOnlyPropertyException("Class1.Message can not be changed once it is set.");
        }
        message = value;
    }
}
Run Code Online (Sandbox Code Playgroud)

从以下VB6/VBA代码中使用它

Dim cls As New Rubberduck_SourceControl.Class1
cls.Message = "Hello"
Debug.Print cls.Message

cls.Message = "Goodbye" 'we expect a read only error here
Debug.Print cls.Message
Run Code Online (Sandbox Code Playgroud)

导致错误的结果.

vb错误对话框

设置后,有效地使属性为只读属性.


但这会导致客户端遇到运行时错误.这里的解决方案是创建一个类工厂,如@Hans Passant建议的那样.

理解COM理念很重要.COM提供了一个类工厂来创建对象,但它不支持传递任意参数.这就是为什么你总是需要一个默认的构造函数.好吧,没问题,只需创建自己的工厂.隐藏Class1并编写Class1Factory类.使用返回_Class1的CreateClass1()方法.它可以采取你需要的任何参数.

所以,我按照原来的方式实现了这个类.(没有默认的构造函数和private set属性.重要的是要注意该接口只有一个get属性.

[Guid("B1E17DF6-9578-4D24-B578-9C70979E2F05")]
public interface _Class1
{

    [DispId(2)]
    string Message { get; }

    [DispId(1)]
    string TestingAMethod();
}

[Guid("197A7A57-E59F-41C9-82C8-A2F051ABA53C")]
[ProgId("Project.Class1")]
[ClassInterface(ClassInterfaceType.None)]
public class Class1 : _Class1
{
    public string Message { get; private set; }

    public Class1(string message)
    {
        this.Message = message;
    }

    public string TestingAMethod()
    {
        return "Hello World";
    }
}
Run Code Online (Sandbox Code Playgroud)

然后我创建了一个具有默认构造函数的类工厂,接受与构造函数相同的参数Class1.

[Guid("98F2287A-1DA3-4CC2-B808-19C0BE976C08")]
public interface _ClassFactory
{
    Class1 CreateClass1(string message);
}

[Guid("C7546E1F-E1DB-423B-894C-CB19607972F5")]
[ProgId("Project.ClassFactory")]
[ClassInterface(ClassInterfaceType.None)]
public class ClassFactory : _ClassFactory
{
    public Class1 CreateClass1(string message)
    {
        return new Class1(message);
    }
}
Run Code Online (Sandbox Code Playgroud)

所以,现在如果客户端试图设置Message属性可言,它得到一个编译时错误信息.

编译错误:无法分配给只读属性