Как в универсальной структуре VB.NET получить доступ к ее явно предоставленному конструктору?

Прежде всего, чего я хочу добиться:

Я хочу расширить тип данных значения, предоставив дополнительные свойства, особенно для проверки диапазонов, предоставленных во время объявления. Я хочу, чтобы новый тип данных также был типом значения.

Сравните с Адой:

subtype Day_Number is Integer range 1 .. 31;

Идеальным, но явно невыполнимым, было бы:

Dim DayNumber As Int64 Range 1 To 31

Тем не менее, я был бы рад:

Dim DayNumber As RangeInt64(1, 31)

Не беда, если инициализация не торопится. После предоставления диапазонов они считаются неизменяемыми. С этого момента тип данных используется только для установки/получения значений, как и с обычными типами значений, только в том случае, если они подлежат проверке на соответствие изначально предоставленному диапазону.

Моя попытка:

Поскольку я не могу наследовать от структур, чтобы расширить их, я попытался включить структуру в структуру в качестве члена.

В модуле у меня есть эта структура:

Friend Structure SRangeValueType(Of T)
    Private lMinimum As T
    Private lMaximum As T

    Friend Property Minimum As T
        Get
            Return lMinimum
        End Get
        Set(tValue As T)
            lMinimum = tValue
        End Set
    End Property

    Friend Property Maximum As T
        Get
            Return lMaximum
        End Get
        Set(tValue As T)
            lMaximum = tValue
        End Set
    End Property

    Friend Sub New(Minimum As T, Maximum As T)
        lMinimum = Minimum
        lMaximum = Maximum
    End Sub
End Structure

Я пытаюсь использовать эту общую структуру как член другой структуры (конкретного типа Int64):

Public Structure RangeInt64
    Private Range As SRangeValueType(Of Int64)
End Structure

Однако при этом не используется конструктор с двумя аргументами.

Скажем, я хочу инициализировать Range (единственный член структуры RangeInt64) со значениями 100 и 200 для минимума и максимума, соответственно.

Мне не разрешено использовать что-то вроде:

    Private Range As SRangeValueType(Of Int64)(100,200)

Каков правильный синтаксис для предоставления моих значений универсальному конструктору?

Добавьте New так что, Private Range As New SRangeValueType(Of Int64)(100, 200)... ИМХО, вам нужно посмотреть на ваши сеттеры, сейчас они не правильно установлены... Например, lMinimum = Value должно быть lMinimum = tValue.

Trevor 08.04.2019 15:39

Если вы хотите вызвать конструктор, вы должны явно использовать ключевое слово New. Если бы RangeInt64 был классом, вы могли бы использовать Private Range As New SRangeValueType(Of Int64)(100, 200), но вы не можете инициализировать поля структуры таким образом, так что вам в основном не повезло. Если вы настаиваете на использовании структуры, вам нужно вызвать метод для инициализации поля. Проще говоря, вам, вероятно, следует использовать классы, а не структуры.

jmcilhinney 08.04.2019 15:41

@jmcilhinney Почему вы говорите, что не можете сделать это со структурой? Вы можете создавать конструкторы не по умолчанию для структур. Возьмем, к примеру, Dim x As New Point(1, 2)...

Steven Doggart 08.04.2019 15:43

@StevenDoggart, я ни разу не сказал, что нельзя объявить конструктор для структуры. Я сказал, что вы не можете инициализировать поле структуры, где оно объявлено. Я специально сказал, что вы не можете использовать Private Range As New SRangeValueType(Of Int64)(100, 200) в RangeInt64, потому что RangeInt64 — это структура. Если бы RangeInt64 был классом, то эта строка была бы совершенно законной. Я говорю, что RangeInt64 должен быть классом по этой причине, а SRangeValueType должен быть классом, потому что он изменчив.

jmcilhinney 08.04.2019 15:47
«Мне нужен тип значения, а не ссылочный тип». И я хочу миллион долларов.
jmcilhinney 08.04.2019 15:48

@jmcilhinney А. Я пропустил ту часть, где он делал это из другой структуры. К сожалению :)

Steven Doggart 08.04.2019 16:19

@jmcilhinney, оказывается, обходных путей и ограничений слишком много, чтобы настаивать на типе значения, поэтому я преобразовал это в класс, точно так же, как вы пытались убедить меня с самого начала.

Herb 11.04.2019 09:13
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
7
744
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Обычно, если вы добавляете конструктор в структуру, вы можете вызвать его с помощью ключевого слова New:

Dim x As SRangeValueType(Of Int64)  ' Calls the default, infered, parameter-less constructor
Dim y As New SRangeValueType(Of Int64)(100, 200)  ' Calls the explicitly defined constructor

Однако проблема не в этом. Проблема в том, что вы пытаетесь установить значение по умолчанию для неразделяемого поля в структуре. Это то, что никогда не допускается. Все не общие поля в структурах должны по умолчанию иметь значение по умолчанию (т. е. Nothing). Например:

Public Structure RangeInt64
    Private x As Integer = 5  ' Error: Initializers on structure members are valid only for 'Shared' members and constants
    Private y As New StringBuilder()  ' Error: Non-shared members in a structure cannot be declared 'New'
End Structure

И, как вы, возможно, уже знаете, вы также не можете переопределить предполагаемый конструктор без параметров по умолчанию для структуры:

Public Structure RangeInt64
    Public Sub New()  ' Error: Structures cannot declare a non-shared 'Sub New' with no parameters
        x = 5
        y = New StringBuilder()
    End Sub

    Private x As Integer
    Private y As StringBuilder
End Structure

Таким образом, вы застряли. По задумке, когда используется конструктор по умолчанию, все поля в структуре всегда должны по умолчанию иметь значение Nothing. Однако, если вам действительно нужно, чтобы это была структура, и вы не можете просто преобразовать ее в класс, и вам действительно нужно изменить ее значение по умолчанию, вы теоретически можете подделать ее, чтобы она работала, используя свойство для переноса поле:

Public Structure RangeInt64
    Private _Range As SRangeValueType(Of Int64)
    Private _RangeInitialized As Boolean
    Private Property Range As SRangeValueType(Of Int64)
        Get
            If Not _RangeInitialized Then
                _Range = New SRangeValueType(Of Int64)(100, 200)
                _RangeInitialized = True
            End If
            Return _Range
        End Get
        Set(value As SRangeValueType(Of Int64))
            _Range = value
        End Set
    End Property
End Structure

Однако само собой разумеется, что это довольно грубо, и его следует избегать, если это возможно.

Обновлять

Теперь, когда вы предоставили более подробную информацию о том, чего вы пытаетесь достичь, я думаю, что у меня может быть лучшее решение для вас. Вы правы в том, что структуры не поддерживают наследование, но поддерживают интерфейсы. Итак, если все, что вам нужно, это набор типов диапазонов, по одному на тип значения, все с одинаковыми минимальными и максимальными свойствами, но все возвращающие разные заранее определенные значения, тогда вы можете сделать что-то вроде этого:

Private Interface IRangeValueType(Of T)
    ReadOnly Property Minimum As T
    ReadOnly Property Maximum As T
End Interface

Private Structure RangeInt64
    Implements IRangeValueType(Of Int64)

    Public ReadOnly Property Minimum As Long Implements IRangeValueType(Of Int64).Minimum
        Get
            Return 100
        End Get
    End Property

    Public ReadOnly Property Maximum As Long Implements IRangeValueType(Of Int64).Maximum
        Get
            Return 200
        End Get
    End Property
End Structure

Вы забыли установить _RangeInitialized в геттере. Кроме того, свойство должно быть Public.

jmcilhinney 08.04.2019 16:21

Упс. Фиксированный. Я не сделал это общедоступным, потому что это не было общедоступным в примере, приведенном в вопросе.

Steven Doggart 08.04.2019 16:29
«это не было общедоступным в примере, приведенном в вопросе». Хорошая мысль, хотя кажется странным, что это так. Я предполагаю, что в коде есть что-то еще, что нам не показывают, что, кажется, усиливает аргументы в пользу использования классов. Если это поле действительно используется только внутренне, то установка его после создания объекта в любом случае не является проблемой.
jmcilhinney 08.04.2019 16:34

@jmcilhinney, я обновил свой вопрос, указав мотивацию его задать, возможно, есть какой-то другой подход, использующий тип значения для моего намерения. - С другой стороны, почему за этот ответ проголосовали отрицательно? Объяснения и предоставление обходного пути кажутся значимыми.

Herb 09.04.2019 10:03

@Herb Я обновил свой ответ в ответ на обновления вашего вопроса. Что касается того, почему мой ответ был отклонен, я предполагаю, что это просто потому, что кто-то посчитал мой уродливый обходной путь слишком уродливым и что мне не следовало даже упоминать об этом. Способ реализации этого обходного пути, безусловно, является неожиданным поведением, что почти всегда нежелательно, и он немного менее эффективен при каждом чтении, но я не верю, что в этом есть что-то опасное по своей сути, иначе я бы не поделился им.

Steven Doggart 09.04.2019 14:15

Эмпирическое правило: никогда не создавайте изменяемые структуры. Однако в случае моего обходного пути, поскольку неинициализированная структура и инициализированная структура будут неразличимы для использования кода, это никогда не приведет к непредвиденным результатам, так что это своего рода исключение из правила. Несмотря на это, я знал, что обходной путь вызовет у людей беспокойство, но я решил, что мой ответ в целом будет вам полезен, поэтому я решил не удалять его.

Steven Doggart 09.04.2019 14:19

Другие вопросы по теме