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

时间:2015-02-12 23:31:23

标签: c# properties com 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";
    }
}

通常,我会将该属性声明为:

public string Message { get; private set; }

但这显然不会起作用,因为COM无法使用接受参数的构造函数。

所以,问题是:

如何在不使用private setreadonly的情况下确保仅设置属性一次?

2 个答案:

答案 0 :(得分:7)

根据this article on Code Project

  

如果方法调用失败或业务逻辑验证失败,则.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;
    }
}

从以下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

导致错误。

vb error dialog

有效地将属性设置为只读属性。


但这会导致客户端遇到运行时错误。这里的解决方案是创建一个类@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";
    }
}

然后我创建了一个类工厂, 有一个默认构造函数,并且接受与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);
    }
}

因此,现在,如果客户端尝试设置Message属性,则会出现 编译时 错误

compile error: can't assign to read only property

答案 1 :(得分:1)

它不漂亮,并没有暴露出它只读"这样的事实,但它会像私人跟踪bool那样工作吗?

[Guid("197A7A57-E59F-41C9-82C8-A2F051ABA53C")]
[ProgId("Project.Class1")]
[ClassInterface(ClassInterfaceType.None)]
public class Class1 : _Class1
{
    private bool _isMessageSet = false;
    private string _message;

    public string Message 
    { 
        get { return _message; }
        set
        {
            if (!_isMessageSet)
            {
                _message = value;
                _isMessageSet = true;
            }
        }
    }

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

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