COM Interop:如何使C#属性可用作VBA中的VARIANT

Ren*_*uis 8 .net c# com vba interop

假设我有一个C#Nullable DateTime?属性需要由VBA通过COM使用.

    public DateTime? TestDate {
        get ; set;
    }
Run Code Online (Sandbox Code Playgroud)

不幸的是,通过COM看不到Nullables,所以我希望该属性返回一个可以被视为VBA Variant的东西.

不幸的是,我不确定应该怎么写.

我尝试使用a object和a dynamic而不是DateTime?:虽然我可以获取属性值,但我无法设置它(我从VBA 获得运行时错误'424'对象所需的错误).

注意:我没有让我的库COM可见的问题:一切正常,我可以毫无问题地使用VBA中的.Net类型,除了这个特殊问题.

谢谢你的任何指示.

编辑:我发现了一个有趣的页面,描述了对象默认编组,但我似乎无法解释为什么我声明为无法设置我的属性object.
我错过了什么.

Ren*_*uis 8

在这里我做了解决这个问题的方法.

对象属性

public object MyObject {
    get ; set;
}
Run Code Online (Sandbox Code Playgroud)

Object从VBA 使用.Net 属性时,读取属性是没有问题的,它将被正确地视为一个Variant.
不幸的是,尝试从VBA设置属性将失败.

但是,使用普通方法可以正常工作:

private object _MyObject;

public object GetMyObject() {
    return _MyObject;
}

public void SetMyObject(object value) {
    if (value == DbNull.Value)
        value = null;   
    _MyObject = value;
}
Run Code Online (Sandbox Code Playgroud)

检查DBNull是为了解决VBA' Null实际编组为DBNull.Net的问题.

约会时间?

现在,为了使可空的DateTime?工作,我们可以做类似的事情:

private DateTime? _MyDate;

public object GetMyDate() {
    return _MyDate
}

public void SetMyDate(object value) {
    if (value == null || value == DbNull.Value)
        _MyDate = null;   
    else
        _MyDate = (DateTime?)value;
}
Run Code Online (Sandbox Code Playgroud)

在VBA中,我们可以在属性中隐藏这些get/set(假设我们有一个类的现有实例myclassinstance):

Public Property Get MyDate() As Variant
    MyDate = myclassinstance.GetMyDate()
End Property

Public Property Set MyDate(value as Variant)
    myclassinstance.SetMyDate value
End Property
Run Code Online (Sandbox Code Playgroud)

一种更通用的方式

这有点难看,因为我们的C#类MyDate作为GetMyDate/SetMyDate方法而不是属性公开.
要以更通用的方式实现它,以便该机制可用于我们类中的所有属性,我们可以使用a Dictionary作为后备存储:

[ClassInterface(ClassInterfaceType.AutoDual)]
[ComVisible(true)]
public class MyClass {

    private Dictionary<string,object> backingStore = new Dictionary<string,object>();

    public object GetPropertyValue(string propertyName) {
        if (backingStore.ContainsKey(propertyName))
            return backingStore[propertyName];
        else
            return null
    }

    public void SetPropertyValue(string propertyName, object value) {
        if (value == DBNull.Value) value = null;
        if (backingStore.ContainsKey(propertyName))
            backingStore[propertyName] = value;
        else
            backingStore.Add(propertyName, value);
    }

    [ComVisible(false)]
    public DateTime? MyDate {
        get {
            return GetPropertyValue(@"MyDate") ?? default(DateTime?);
        }
        set {
            SetPropertyValue(@"MyDate", value);
        }            
    }
}
Run Code Online (Sandbox Code Playgroud)

ComVisible(false)属性确保从VBA中看不到属性.

在VBA中,我们声明属性:

Public Property Get MyDate() As Variant
    MyDate = myclassinstance.GetPropertyValue("MyDate")
End Property

Public Property Set MyDate(value as Variant)
    myclassinstance.SetPropertyValue "MyDate", value
End Property
Run Code Online (Sandbox Code Playgroud)