2015-02-12 3 views
3

У меня есть класс C#, который я бы хотел открыть для VB6 через COM. Проблема состоит в том, что для этого класса требуется конструктор по умолчанию. Таким образом, я должен выставить сеттеры клиенту, чтобы эти свойства можно было начинать с начала.Как создать свойство только для чтения, открытое COM?

Например:

[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 set или readonly?

+5

Понимание философии COM имеет важное значение. COM предоставляет фабрику классов для создания объектов, но не поддерживает передачу произвольных аргументов. Вот почему вам всегда нужен конструктор по умолчанию. Ну, не проблема, просто создайте свой собственный завод. Скройте класс1 и напишите класс Class1Factory. С помощью метода CreateClass1(), который возвращает _Class1. Он может принимать любые аргументы, которые вам нужны. –

+0

Вы хотите создать завод на стороне клиента @HansPassant? – RubberDuck

+0

Нет, на вашем сервере. –

ответ

7

По this article on Code Project:

Если вызов метода не удается или бизнес-логику проверки не удается, компонент .NET, как ожидается, вызовет исключение. Это исключение обычно имеет сбой HRESULT, присвоенный ему, и связанное с ним описание ошибки . CCW извлекает данные, такие как Код ошибки, Сообщение об ошибке и т. Д. Из .NET Exception и предоставляет эти данные в форме, которая может быть использована COM-клиентом.

Emphasis mine.

Итак, что мы можем проверить, чтобы узнать, установлено ли частное поле. Если это было, выбросьте исключение.

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 предоставляет фабрику классов для создания объектов, но не поддерживает передачу произвольных аргументов. Вот почему вам всегда нужен конструктор по умолчанию. Ну, не проблема, просто создайте свой собственный завод. Скройте класс1 и напишите класс Class1Factory. С помощью метода CreateClass1(), который возвращает _Class1.Он может принимать любые аргументы, которые вам нужны.

Итак, я реализовал класс так, как я хотел первоначально. (С конструктором не по умолчанию и 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 «s.

[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

Это некрасиво и не раскрывает тот факт, что это «только для чтения», но будет ли что-то вроде частного отслеживания 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; 
    } 
} 
+0

Это сработает, но вы правы. Это некрасиво. Клиентский код будет перекликаться, не подозревая, что свойство осталось прежним. Это действительно дает мне представление. Будьте на связи. * Бежит, чтобы сделать больше исследований. * – RubberDuck

+0

@ RubberDuck вы будете отправлять назад, когда придумаете решение? Я хотел бы посмотреть, как вы это решаете! –

+0

Ох. Да @ RufusL. Я уже оставил ответ на этот вопрос. «ClassFactory» отлично решал мою проблему. – RubberDuck

Смежные вопросы