GOTCHA #1 Размеры типов псевдонимов могут отличаться

среда, 29 сентября 2010, Александр Краковецкий

Это перевод "GOTCHA #1 Type alias size doesn't match what you're familiar with" из книги ".NET Gotchas".

Спецификация Common Language Specification (CLS) описывает правила для того, чтобы типы, написанные на разных языках, были совместимыми. Common Type System (CTS) предоставляет кросс-языковую интеграцию, безопасность типов и высокопроизводительное выполнение управляемого кода. Однако, не все типы, которые поддерживаются в .NET Framework, являются CLS-совместимыми. При разработке библиотеки классов убедитесь, что все типы, которые вы используете, являются CLS-совместимыми. Вы можете проверить свой код с помощью инструмента FxCop, чтобы убедиться, что он соответствует "Design Guidelines for Class Library Developers".

Для удобства C++, Java и VB6 программистов, семейство языков .NET содержат псевдонимы (алиасы) для CTS-типов, такие как int и long в C # и Integer и Long в VB.NET.

Если вы C++ программист, вы можете предложить, что long в C# соответствует long в C++. Если Вы пришли из мира VB6, то можете подумать, что Integer и Long в VB.NET эквивалентны Integer и Long в VB6. Но в обоих случаях вы ошиблись.

Давайте посмотрим на пример. Как проиграть звуковой сигнал? Одна из возможностей заключается в использовании PInvoke и вызове Win32 метода Beep(). PInvoke позволяет управляемому коду вызывать неуправляемые функции. В VB6, Beep() имеет следующее объявление:

Beep(dwFreq as Long, dwDuration as Long) as Boolean

Тип Long в VB6 является 32-битным int.

Прототип Win32 будет иметь вид:

Beep(dwFreq as DWORD, dwDuration as DWORD) as Boolean

Теперь попробуем использовать Beep() в .NET. Примеры кода приведен ниже:

C#:

using System;
using System.Runtime.InteropServices;

namespace InvokeBeep
{
    class Test
    {
        [DllImport("kernel32")]
        public static extern bool Beep(long dwFreq,
            long dwDuration);

        [STAThread]
        static void Main(string[] args)
        {
            Beep(1000, 1000);
        }
    }
}

VB.NET:

Imports System.Runtime.InteropServices

Module Module1

    Public Declare Function Beep Lib "Kernel32" ( _
        ByVal dwFreq As Long, ByVal dwDuration As Long) As Boolean

    Sub Main()
        Beep(1000, 1000)
    End Sub

End Module

Если вы скомпилируете это приложение, то увидите, что ничего не произошло. В чем же причина?

Несмотря на то, что CTS определяет типы данных, доступные для всех .NET языков, например System.Int32 или System.Double, каждый .NET язык определяет свои псевдонимы для этих типов. Например, C # использует int как псевдоним для System.Int32, в то время как VB.NET использует Integer. Эти псевдонимы, по всей видимости, соответствуют типам C++ (в случае C #) и VB6 (в случае VB.NET).

Таблица 1 показывает CTS-типы и их размеры для C# и C++.

CTS тип Размер C# псевдоним Эквивалентный тип в C++
System.Int32 4 bytes int int или long
System.Int64 8 bytes long _ _int64
System.Char 2 bytes char WCHAR
System.Double 8 bytes double double

Таблица 2 показывает CTS-типы и их размеры для VB.NET и VB6.

CTS тип Размер VB.NET псевдоним Эквивалентный тип в VB
System.Int32 4 bytes Integer Long
System.Int64 8 bytes Long N/A
System.Char 2 bytes Char String * 1
System.Double 8 bytes Double Double

Как вы можете видеть из этих таблиц, размер long в C++ отличается от размера long в C#. Также размер Long в VB6 отличается от размера Long в VB.NET. Long в VB6 соответствует Integer в VB.NET. В нашем примере мы на самом деле передали 64-битный аргумент в функцию, которая ожидала 32-битный параметр, из-за чего наш метод не выполнился корректно. В примере ниже эта проблема исправлена.

В теории, отправка Integer в VB.NET не вполне является точным соответствием. Beep() принимает два беззнаковых 32-разрядных (DWORD) аргументов. Integer в VB.NET - это знаковый 32-разрядное число. Псевдонима для System.UInt32 в VB.NET нет (тип System.UInt32 не является CLS-совместимым). Можно использовать System.UInt32 в качестве типа параметра , передаваемого в метод Beep().

Новый вариант будет иметь вид:

C#:

[DllImport("kernel32")]
public static extern bool Beep(uint dwFreq, uint dwDuration);


VB.NET:

Public Declare Function Beep Lib "Kernel32" ( _
ByVal dwFreq As Integer, ByVal dwDuration As Integer) _
As Boolean

Вывод

Помните о типах и их размерах, которые вы используете в .NET приложениях. Сайт http://pinvoke.net может помочь вам найти правильные соответствия.

P.S. Если вы хотите принять участие в переводе других gotchas, можете писать на электронную почту или в комментариях.

Компании из статьи


Microsoft Украина


Сайт:
http://www.microsoft.com/ukr/ua/

Microsoft Украина Украинское подразделение компании Microsoft.

Ищите нас в интернетах!

Комментарии

Свежие вакансии