Compartilhar via


Alterações na área de transferência do Windows Forms e no DataObject no .NET 10

Este artigo mostra como atualizar suas operações de área de transferência e arrastar e soltar do Windows Forms para as novas APIs com verificação de tipo no .NET 10. Você aprenderá a usar os métodos Clipboard.TryGetData e Clipboard.SetDataAsJson<T>(String, T), entender quais tipos internos funcionam sem alterações, e descobrir estratégias para lidar com tipos personalizados e dados de legado após a remoção de BinaryFormatter.

BinaryFormatter foi removido do runtime no .NET 9 devido a vulnerabilidades de segurança. Esta mudança interrompeu as operações de copiar e arrastar e soltar com objetos personalizados. O .NET 10 apresenta novas APIs que usam a serialização JSON e métodos de segurança de tipo para restaurar essa funcionalidade, melhorar a segurança e fornecer melhor tratamento de erros e compatibilidade entre processos.

Clipboard.SetData(String, Object) não funciona mais com tipos personalizados que exigem BinaryFormatter para serialização. Esse método executa uma operação de cópia (por padrão) que serializa os dados imediatamente, mas essa serialização agora falha para tipos que precisam BinaryFormatter porque foram removidos. Clipboard.GetData(String) está obsoleto no .NET 10. Quando BinaryFormatter é necessário para desserialização, mas não está habilitado, GetData() retorna uma NotSupportedException instância. Use os novos métodos Clipboard.TryGetData e Clipboard.SetDataAsJson<T>(String, T) para operações de tipo seguro e serialização JSON de objetos personalizados.

As seções a seguir fornecem diretrizes de migração detalhadas, explicam quais tipos funcionam sem alterações e mostram como lidar com novos cenários de desenvolvimento e dados herdados.

Pré-requisitos

Antes de continuar, examine estes conceitos:

  • Como os aplicativos utilizavam BinaryFormatter em cenários de área de transferência e de arrastar e soltar antes do .NET 9.
  • As vulnerabilidades de segurança que levaram à remoção de BinaryFormatter.
  • Como trabalhar com System.Text.Json padrões de serialização e suas limitações.

Para obter mais informações, consulte estes artigos:

Alterações disruptivas com a remoção do BinaryFormatter

A remoção do BinaryFormatter no .NET 9 altera fundamentalmente a forma como o Windows Forms lida com as operações de copiar/colar e de arrastar e soltar com tipos personalizados. Essas alterações afetam os padrões de código existentes e exigem uma migração cuidadosa para manter a funcionalidade.

Os tipos personalizados não são mais serializados automaticamente

No .NET 8 e versões anteriores, você podia colocar qualquer objeto personalizado serializável na área de transferência chamando SetData(). BinaryFormatter lidou com a serialização automaticamente. A partir do .NET 9, SetData() ainda executa uma operação de cópia que serializa os dados imediatamente, mas essa serialização falha para tipos que exigem BinaryFormatter porque foram removidos.

O código a seguir não funciona mais:

[Serializable]
public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

public static void BrokenCustomTypeExample()
{
    // This worked in .NET 8 and earlier but silently fails starting with .NET 9
    Person person = new Person { Name = "John", Age = 30 };
    Clipboard.SetData("MyApp.Person", person);  // No data is stored

    // Later attempts to retrieve the data return a NotSupportedException instance
    object data = Clipboard.GetData("MyApp.Person");
}
<Serializable>
Public Class Person
    Public Property Name As String
    Public Property Age As Integer
End Class

Public Shared Sub BrokenCustomTypeExample()
    ' This worked in .NET 8 and earlier but silently fails starting with .NET 9
    Dim person As New Person With {.Name = "John", .Age = 30}
    Clipboard.SetData("MyApp.Person", person)  ' No data is stored

    ' Later attempts to retrieve the data return a NotSupportedException instance
    Dim data As Object = Clipboard.GetData("MyApp.Person")
End Sub

O que você pode ver

  • O SetData() método é concluído sem gerar uma exceção.
  • Os dados são colocados na área de transferência, mas o processo de serialização falha para tipos que exigem BinaryFormatter.
  • Posteriormente, tenta recuperar os dados com GetData() retorna uma instância de NotSupportedException que indica que BinaryFormatter é exigida mas não está habilitada.

Diretrizes de migração

Use o novo método SetDataAsJson<T>() ou serialize manualmente em um string ou byte[]. Para obter detalhes, consulte a seção Trabalhar com tipos personalizados .

GetData() é obsoleto – use TryGetData<T>() em vez disso

O método herdado GetData() é obsoleto no .NET 10. Esse método retorna dados com sucesso na maioria dos casos, mas quando BinaryFormatter é necessário para desserialização e não está habilitado, GetData() retorna uma instância NotSupportedException que indica que BinaryFormatter é necessária. Você deve migrar para os novos métodos de tipo seguro TryGetData<T>() para melhor tratamento de erros e segurança de tipo.

O exemplo a seguir mostra o padrão obsoleto que você deve evitar:

public static void ObsoleteGetDataExample()
{
    // Don't use - GetData() is obsolete in .NET 10
    object data = Clipboard.GetData("MyApp.Person");  // Obsolete method

    // Returns a NotSupportedException instance for a custom object type
    if (data is Person person)
    {
        Console.WriteLine($"Processing person: {person.Name}, Age: {person.Age}");
    }
}
Public Shared Sub ObsoleteGetDataExample()
    ' Don't use - GetData() is obsolete in .NET 10
    Dim data As Object = Clipboard.GetData("MyApp.Person")  ' Obsolete method

    ' Returns a NotSupportedException instance for a custom object type
    If TypeOf data Is Person Then
        Dim person As Person = DirectCast(data, Person)
        Console.WriteLine($"Processing person: {person.Name}, Age: {person.Age}")
    End If
End Sub

Em vez disso, use a abordagem moderna de tipo seguro com TryGetData<T>():

public static void ModernTryGetDataExample()
{
    var data = new Person { Name = "Alice", Age = 28 };
    Clipboard.SetDataAsJson("MyAppData", data);

    // Use this - type-safe approach with TryGetData<T>()
    if (Clipboard.TryGetData("MyApp.Person", out Person person))
    {
        // person is guaranteed to be the correct type
        Console.WriteLine($"Processing person: {person.Name}, Age: {person.Age}");
    }
    else
    {
        // Handle the case where data isn't available or is the wrong type
        MessageBox.Show("Unable to retrieve person data from clipboard");
    }
}
Public Shared Sub ModernTryGetDataExample()
    Dim data As New Person With {.Name = "Alice", .Age = 30}
    Clipboard.SetDataAsJson("MyAppData", data)

    ' Use this - type-safe approach with TryGetData(Of T)()
    Dim person As Person = Nothing
    If Clipboard.TryGetData("MyApp.Person", person) Then
        ' person is guaranteed to be the correct type
        Console.WriteLine($"Processing person: {person.Name}, Age: {person.Age}")
    Else
        ' Handle the case where data isn't available or is the wrong type
        MessageBox.Show("Unable to retrieve person data from clipboard")
    End If
End Sub

Benefícios do TryGetData<T>()

  • Segurança do tipo: não há necessidade de conversão – o método retorna o tipo exato que você solicita.
  • Tratamento de erro claro: retorna um indicador de êxito booliano em vez de usar padrões nulos ou de exceção.
  • À prova de futuro: projetado para funcionar com novos métodos de serialização e suporte a dados legados.

Como identificar o código afetado

Procurar por:

  • Quaisquer chamadas de GetData(), já que o método como um todo está obsoleto independentemente do tipo de dados.
  • DataObject.GetData() e IDataObject.GetData() uso em operações de arrastar e soltar.

Diretrizes de migração

Substitua todo o uso de GetData() por métodos TryGetData<T>() com segurança de tipo. Para obter exemplos abrangentes de todas as sobrecargas, consulte a seção Novas APIs de segurança de tipo .

Novas APIs tipo seguro

O .NET 10 apresenta três novas famílias de API que fornecem segurança de tipo, melhor tratamento de erros e suporte para serialização JSON em operações de área de transferência e de arrastar e soltar.

Métodos TryGetData<T>()

A TryGetData<T>() família substitui o método obsoleto GetData() . Ele oferece recuperação segura de tipos e uma indicação clara de sucesso ou falha para suas operações de área de transferência.

Recuperação básica com segurança de tipo

public static void BasicTypeSafeRetrievalExamples()
{
    // Retrieve text data using a standard format
    if (Clipboard.TryGetData(DataFormats.Text, out string textData))
        Console.WriteLine($"Text: {textData}");

    // Retrieve an integer using a custom format
    if (Clipboard.TryGetData("NumberData", out int numberData))
        Console.WriteLine($"Number: {numberData}");

    // Retrieve Unicode text using a standard format
    if (Clipboard.TryGetData(DataFormats.UnicodeText, out string unicodeText))
        Console.WriteLine($"Unicode: {unicodeText}");

    // Retrieve raw text data with OLE conversion control
    if (Clipboard.TryGetData(DataFormats.Text, out string rawText))
        Console.WriteLine($"Raw: {rawText}");

    // Retrieve file drops using a standard format
    if (Clipboard.TryGetData(DataFormats.FileDrop, out string[] files))
        Console.WriteLine($"Files: {string.Join(", ", files)}");
}
Public Shared Sub BasicTypeSafeRetrievalExamples()
    ' Retrieve text data using a standard format
    Dim textData As String = Nothing
    If Clipboard.TryGetData(DataFormats.Text, textData) Then
        Console.WriteLine($"Text: {textData}")
    End If

    ' Retrieve an integer using a custom format
    Dim numberData As Integer
    If Clipboard.TryGetData("NumberData", numberData) Then
        Console.WriteLine($"Number: {numberData}")
    End If

    ' Retrieve Unicode text using a standard format
    Dim unicodeText As String = Nothing
    If Clipboard.TryGetData(DataFormats.UnicodeText, unicodeText) Then
        Console.WriteLine($"Unicode: {unicodeText}")
    End If

    ' Retrieve raw text data with OLE conversion control
    Dim rawText As String = Nothing
    If Clipboard.TryGetData(DataFormats.Text, rawText) Then
        Console.WriteLine($"Raw: {rawText}")
    End If

    ' Retrieve file drops using a standard format
    Dim files As String() = Nothing
    If Clipboard.TryGetData(DataFormats.FileDrop, files) Then
        Console.WriteLine($"Files: {String.Join(", ", files)}")
    End If
End Sub

Tipos JSON personalizados

public static void CustomJsonTypesExamples()
{
    // Retrieve a custom type stored with SetDataAsJson<T>()
    if (Clipboard.TryGetData("Person", out Person person))
        Console.WriteLine($"Person: {person.Name}");

    // Retrieve application-specific data formats
    if (Clipboard.TryGetData("MyApp.Settings", out AppSettings settings))
        Console.WriteLine($"Settings: {settings.Theme}");

    // Retrieve complex custom objects
    if (Clipboard.TryGetData("DocumentData", out DocumentInfo doc))
        Console.WriteLine($"Document: {doc.Title}");
}
Public Shared Sub CustomJsonTypesExamples()
    ' Retrieve a custom type stored with SetDataAsJson(Of T)()
    Dim person As Person = Nothing
    If Clipboard.TryGetData("Person", person) Then
        Console.WriteLine($"Person: {person.Name}")
    End If

    ' Retrieve application-specific data formats
    Dim settings As AppSettings = Nothing
    If Clipboard.TryGetData("MyApp.Settings", settings) Then
        Console.WriteLine($"Settings: {settings.Theme}")
    End If

    ' Retrieve complex custom objects
    Dim doc As DocumentInfo = Nothing
    If Clipboard.TryGetData("DocumentData", doc) Then
        Console.WriteLine($"Document: {doc.Title}")
    End If
End Sub

Métodos SetDataAsJson<T>()

Esses métodos fornecem serialização JSON automática com System.Text.Json usando o armazenamento com segurança de tipo.

Especificar um formato personalizado

public static void CustomFormatExample()
{
    var settings = new AppSettings { Theme = "Dark", AutoSave = true };

    // Use a custom format for better organization
    Clipboard.SetDataAsJson("MyApp.Settings", settings);
}
Public Shared Sub CustomFormatExample()
    Dim settings As New AppSettings With {.Theme = "Dark", .AutoSave = True}

    ' Use a custom format for better organization
    Clipboard.SetDataAsJson("MyApp.Settings", settings)
End Sub

Inferência de formato automático

Ao especificar o nome do tipo como o formato de dados, TryGetData<T> é possível inferir o formato automaticamente:

public static void AutomaticFormatInferenceExample()
{
    var person = new Person { Name = "Alice", Age = 25 };

    // Use the type name as the format
    Clipboard.SetDataAsJson(typeof(Person).FullName, person);

    // Retrieve the data and infer the format automatically
    if (Clipboard.TryGetData(out Person retrievedPerson))
    {
        Console.WriteLine($"Retrieved: {retrievedPerson.Name}");
    }
}
Public Shared Sub AutomaticFormatInferenceExample()
    Dim person As New Person With {.Name = "Alice", .Age = 25}

    ' Use the type name as the format
    Clipboard.SetDataAsJson(GetType(Person).FullName, person)

    ' Retrieve the data and infer the format automatically
    Dim retrievedPerson As Person = Nothing
    If Clipboard.TryGetData(retrievedPerson) Then
        Console.WriteLine($"Retrieved: {retrievedPerson.Name}")
    End If
End Sub

Interface ITypedDataObject

A ITypedDataObject interface permite operações de arrastar e soltar tipadas ao estender IDataObject com métodos tipados.

A partir do .NET 10, DataObject (um tipo comum em cenários de arrastar e soltar) implementa ITypedDataObject.

Usar ITypedDataObject em cenários de arrastar e soltar

private void OnDragDrop(object sender, DragEventArgs e)
{
    if (e.Data is ITypedDataObject typedData)
    {
        // Retrieve files from drag data using a standard format
        if (typedData.TryGetData(DataFormats.FileDrop, out string[] files))
            Console.WriteLine($"Dropped files: {string.Join(", ", files)}");

        // Retrieve text using a standard format
        if (typedData.TryGetData(DataFormats.Text, out string text))
            Console.WriteLine($"Dropped text: {text}");

        // Retrieve custom items using an application-specific format
        if (typedData.TryGetData("CustomItem", out MyItem item))
            Console.WriteLine($"Dropped custom item: {item.Name} = {item.Value}");
    }
}
Private Sub OnDragDrop(sender As Object, e As DragEventArgs)
    If TypeOf e.Data Is ITypedDataObject Then
        Dim typedData As ITypedDataObject = CType(e.Data, ITypedDataObject)

        ' Retrieve files from drag data using a standard format
        Dim files As String() = Nothing
        If typedData.TryGetData(DataFormats.FileDrop, files) Then
            Console.WriteLine($"Dropped files: {String.Join(", ", files)}")
        End If

        ' Retrieve text using a standard format
        Dim text As String = Nothing
        If typedData.TryGetData(DataFormats.Text, text) Then
            Console.WriteLine($"Dropped text: {text}")
        End If

        ' Retrieve custom items using an application-specific format
        Dim item As MyItem = Nothing
        If typedData.TryGetData("CustomItem", item) Then
            Console.WriteLine($"Dropped custom item: {item.Name} = {item.Value}")
        End If
    End If
End Sub

Tipos que não exigem serialização JSON

Muitos tipos de .NET nativos funcionam com operações de área de transferência sem a necessidade de suporte para serialização JSON ou BinaryFormatter. Esses tipos são serializados automaticamente no NRBF (Formato Binário de Comunicação Remota) do .NET, que fornece armazenamento eficiente e mantém a segurança do tipo.

Esses tipos usam o NRBF, o mesmo formato binário eficiente usado pelo herdado BinaryFormatter. A serialização NRBF fornece estes principais benefícios:

  • Representação binária compacta: habilita o armazenamento e a transferência eficientes.
  • Informações de tipo embutido: Preserva os tipos exatos do .NET durante operações de ida e retorno.
  • Compatibilidade entre processos: funciona entre diferentes aplicativos .NET.
  • Serialização automática: tipos serializam sem código personalizado.

Observação

Métodos como SetData() executam uma operação de cópia que serializa os dados imediatamente, garantindo que os dados da área de transferência persistam mesmo após a saída do processo em execução. Se você usar diretamente o tipo DataObject com SetDataObject(dataObject, copy), poderá controlar o momento em que a serialização ocorre. Definir o copy parâmetro como true (o padrão) força a serialização imediata, enquanto false adia a serialização até que seja necessário. Com copy definido como false e os dados são recuperados no mesmo processo, a serialização pode não ser necessária, mas os dados não persistirão após a saída do processo.

Para obter detalhes técnicos, consulte a especificação formato binário de comunicação remota do .NET.

Classes que dão suporte a dados codificados em NRBF são implementadas no System.Formats.Nrbf namespace.

Garantias de segurança de tipo

O Windows Forms fornece vários mecanismos de segurança para esses tipos embutidos.

  • Correspondência exata de tipo. TryGetData retorna apenas o tipo solicitado.
  • Validação automática. O Windows Forms valida a compatibilidade de tipos durante a desserialização.
  • Nenhuma execução arbitrária de código. Ao contrário dos tipos personalizados com BinaryFormatter, esses tipos não podem executar código mal-intencionado.
  • Validação de conteúdo necessária. Você ainda deve validar o conteúdo e os intervalos de dados para a lógica do aplicativo.
  • Sem restrições de tamanho. Matrizes grandes ou bitmaps não são automaticamente limitadas. Monitore o uso de memória.

Tipos primitivos com suporte

Os seguintes tipos primitivos funcionam perfeitamente com o clipboard e operações de DataObject. Você não precisa de serialização ou configuração personalizada. O sistema de área de transferência lida automaticamente com esses tipos internos do .NET:

  • bool, byte, char, decimal, double, short, int e long.
  • sbyte, ushort, uint, ulong, float e string.
  • TimeSpan e DateTime.

Os exemplos a seguir mostram como esses tipos primitivos funcionam diretamente com os métodos SetData() e TryGetData<T>().

public static void PrimitiveTypesExample()
{
    // Numeric types
    Clipboard.SetData("MyInt", 42);
    Clipboard.SetData("MyDouble", 3.14159);
    Clipboard.SetData("MyDecimal", 123.45m);

    // Text and character types
    Clipboard.SetData("MyString", "Hello World");
    Clipboard.SetData("MyChar", 'A');

    // Boolean and date/time types
    Clipboard.SetData("MyBool", true);
    Clipboard.SetData("MyDateTime", DateTime.Now);
    Clipboard.SetData("MyTimeSpan", TimeSpan.FromMinutes(30));

    // Later retrieval with type safety
    if (Clipboard.TryGetData("MyTimeSpan", out TimeSpan value))
    {
        Console.WriteLine($"Clipboard value is: {value}");
    }
}
Public Shared Sub PrimitiveTypesExample()
    ' Numeric types
    Clipboard.SetData("MyInt", 42)
    Clipboard.SetData("MyDouble", 3.14159)
    Clipboard.SetData("MyDecimal", 123.45D)

    ' Text and character types
    Clipboard.SetData("MyString", "Hello World")
    Clipboard.SetData("MyChar", "A"c)

    ' Boolean and date/time types
    Clipboard.SetData("MyBool", True)
    Clipboard.SetData("MyDateTime", DateTime.Now)
    Clipboard.SetData("MyTimeSpan", TimeSpan.FromMinutes(30))

    ' Later retrieval with type safety
    Dim value As TimeSpan

    If Clipboard.TryGetData("MyTimeSpan", value) Then
        Console.WriteLine($"Clipboard value is: {value}")
    End If
End Sub

Coleções de tipos primitivos

Matrizes e listas genéricas de tipos primitivos compatíveis funcionam sem configuração extra. No entanto, tenha essas limitações em mente:

  • Todos os elementos de matriz e lista devem ter suporte para tipos primitivos.
  • Evite string[] e List<string> porque o formato NRBF tem complexidade para lidar com valores nulos em coleções de cadeias de caracteres.
  • Armazene cadeias de caracteres individualmente ou use a serialização JSON para coleções de cadeias de caracteres.

Os exemplos a seguir mostram como matrizes e listas podem ser definidas na área de transferência:

public static void CollectionsExample()
{
    // Arrays of primitive types
    int[] numbers = { 1, 2, 3, 4, 5 };
    Clipboard.SetData("NumberArray", numbers);

    double[] coordinates = { 1.0, 2.5, 3.7 };
    Clipboard.SetData("Coordinates", coordinates);

    // Generic lists
    List<int> intList = new List<int> { 10, 20, 30 };
    Clipboard.SetData("IntList", intList);

    // Retrieval maintains type safety
    if (Clipboard.TryGetData("NumberArray", out int[] retrievedNumbers))
    {
        Console.WriteLine($"Numbers: {string.Join(", ", retrievedNumbers)}");
    }
}
Public Shared Sub CollectionsExample()
    ' Arrays of primitive types
    Dim numbers As Integer() = {1, 2, 3, 4, 5}
    Clipboard.SetData("NumberArray", numbers)

    Dim coordinates As Double() = {1.0, 2.5, 3.7}
    Clipboard.SetData("Coordinates", coordinates)

    ' Generic lists
    Dim intList As New List(Of Integer) From {10, 20, 30}
    Clipboard.SetData("IntList", intList)

    ' Retrieval maintains type safety
    Dim retrievedNumbers As Integer() = Nothing

    If Clipboard.TryGetData("NumberArray", retrievedNumbers) Then
        Console.WriteLine($"Numbers: {String.Join(", ", retrievedNumbers)}")
    End If
End Sub

Tipos de System.Drawing

Tipos de elementos gráficos comuns do System.Drawing namespace funcionam perfeitamente com área de transferência e DataObject operações. Esses tipos são úteis para aplicativos que funcionam com elementos visuais e precisam transferir dados relacionados ao desenho entre componentes ou aplicativos. Lembre-se de que serializar uma Bitmap pode consumir uma grande quantidade de memória, especialmente para imagens grandes. São suportados os seguintes tipos:

  • Point, PointF, Rectangle. RectangleF
  • Size, SizeF. Color.
  • Bitmap (pode consumir memória significativa quando serializado).

Os exemplos a seguir mostram como esses tipos gráficos podem ser usados com operações de transferência de dados:

public static void SystemDrawingTypesExample()
{
    // Geometric types
    Point location = new Point(100, 200);
    Rectangle bounds = new Rectangle(0, 0, 500, 300);
    Size dimensions = new Size(800, 600);

    Clipboard.SetData("Location", location);
    Clipboard.SetData("Bounds", bounds);
    Clipboard.SetData("Size", dimensions);

    // Color information
    Color backgroundColor = Color.FromArgb(255, 128, 64, 192);
    Clipboard.SetData("BackColor", backgroundColor);

    // Bitmap data (use with caution for large images)
    Bitmap smallIcon = new Bitmap(16, 16);
    Clipboard.SetData("Icon", smallIcon);
}
Public Shared Sub SystemDrawingTypesExample()
    ' Geometric types
    Dim location As New Point(100, 200)
    Dim bounds As New Rectangle(0, 0, 500, 300)
    Dim dimensions As New Size(800, 600)

    Clipboard.SetData("Location", location)
    Clipboard.SetData("Bounds", bounds)
    Clipboard.SetData("Size", dimensions)

    ' Color information
    Dim backgroundColor As Color = Color.FromArgb(255, 128, 64, 192)
    Clipboard.SetData("BackColor", backgroundColor)

    ' Bitmap data (use with caution for large images)
    Dim smallIcon As New Bitmap(16, 16)
    Clipboard.SetData("Icon", smallIcon)
End Sub

Trabalhar com tipos personalizados

Quando você usa SetDataAsJson<T>(String, T) e TryGetData com tipos personalizados, System.Text.Json manipula a serialização automaticamente. Muitos tipos funcionam sem nenhuma configuração especial: registros, classes simples e structs com propriedades públicas são serializados perfeitamente.

Tipos simples que funcionam sem atributos

Os tipos personalizados mais simples não exigem configuração especial:

// Records work without any attributes.
public record PersonInfo(string Name, int Age, string Email);

// Simple classes serialize all public properties automatically.
public class DocumentMetadata
{
    public string Title { get; set; }
    public DateTime Created { get; set; }
    public string Author { get; set; }
}

// Structs with public properties work seamlessly.
public struct Point3D
{
    public double X { get; set; }
    public double Y { get; set; }
    public double Z { get; set; }
}
' Simple classes serialize all public properties automatically.
Public Class DocumentMetadata
    Public Property Title As String
    Public Property Created As DateTime
    Public Property Author As String
End Class

' Structs with public properties work seamlessly.
Public Structure Point3D
    Public Property X As Double
    Public Property Y As Double
    Public Property Z As Double
End Structure

Usar atributos JSON para controle avançado

Use System.Text.Json atributos somente quando precisar personalizar o comportamento de serialização. Para obter diretrizes abrangentes sobre System.Text.Json serialização, atributos e opções de configuração avançadas, consulte serialização e desserialização JSON no .NET.

O exemplo a seguir mostra como você pode usar atributos JSON para controlar a serialização:

public class ClipboardFriendlyType
{
    // Include a field that normally isn't serialized
    [JsonInclude]
    private int _privateData;

    // Public properties are always serialized
    public string Name { get; set; }
    
    // Exclude sensitive or non-essential data
    [JsonIgnore]
    public string InternalId { get; set; }
    
    // Handle property name differences for compatibility
    [JsonPropertyName("display_text")]
    public string DisplayText { get; set; }
    
    // Control null value handling
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
    public string OptionalField { get; set; }
}
Public Class ClipboardFriendlyType
    ' Include a field that normally isn't serialized
    <JsonInclude>
    Private _privateData As Integer

    ' Public properties are always serialized
    Public Property Name As String

    ' Exclude sensitive or non-essential data
    <JsonIgnore>
    Public Property InternalId As String

    ' Handle property name differences for compatibility
    <JsonPropertyName("display_text")>
    Public Property DisplayText As String

    ' Control null value handling
    <JsonIgnore(Condition:=JsonIgnoreCondition.WhenWritingNull)>
    Public Property OptionalField As String
End Class

Exemplo: operações de área de transferência com tipos personalizados

public static void CustomTypesClipboardOperationsExample()
{
    var data = new ClipboardFriendlyType 
    { 
        Name = "Sample", 
        DisplayText = "Sample Display Text",
        InternalId = "internal-123" // This property isn't serialized due to [JsonIgnore]
    };

    Clipboard.SetDataAsJson("MyAppData", data);

    if (Clipboard.TryGetData("MyAppData", out ClipboardFriendlyType retrieved))
    {
        Console.WriteLine($"Retrieved: {retrieved.Name}");
        // retrieved.InternalId is null because of [JsonIgnore]
    }
}
Public Shared Sub CustomTypesClipboardOperationsExample()
    Dim data As New ClipboardFriendlyType With {
        .Name = "Sample",
        .DisplayText = "Sample Display Text",
        .InternalId = "internal-123" ' This property isn't serialized due to <JsonIgnore>
    }

    Clipboard.SetDataAsJson("MyAppData", data)

    Dim retrieved As ClipboardFriendlyType = Nothing
    If Clipboard.TryGetData("MyAppData", retrieved) Then
        Console.WriteLine($"Retrieved: {retrieved.Name}")
        ' retrieved.InternalId is null because of <JsonIgnore>
    End If
End Sub

Cuidado

BinaryFormatter O suporte não é recomendado. Use apenas como uma ponte de migração temporária para aplicativos legados que não podem migrar imediatamente para as novas APIs seguras por tipo.

Se você precisar continuar usando BinaryFormatter para operações de área de transferência no .NET 10, habilite suporte limitado por configuração explícita. Essa abordagem traz riscos significativos de segurança e requer várias etapas.

Para obter instruções passo a passo completas, consulte Habilitar suporte à área de transferência BinaryFormatter (não recomendado). Para obter diretrizes gerais de migração, consulte o guia de migração do BinaryFormatter.

Avisos e riscos de segurança

BinaryFormatter é inerentemente inseguro e preterido por estes motivos:

  • Vulnerabilidades arbitrárias de execução de código: os invasores podem executar código mal-intencionado durante a desserialização, expondo seu aplicativo a ataques remotos.
  • Ataques de negação de serviço: dados maliciosos na área de transferência podem consumir memória excessiva ou recursos de CPU, levando a falhas ou instabilidade.
  • Riscos de divulgação de informações: os invasores podem extrair dados confidenciais da memória.
  • Sem limites de segurança: o formato é fundamentalmente inseguro e as configurações não podem protegê-lo.

Habilite esse suporte apenas como uma ponte temporária enquanto você atualiza seu aplicativo para usar as novas APIs com segurança de tipo.

Para obter diretrizes de segurança detalhadas e etapas de configuração, consulte avisos de segurança e riscos no guia de instruções.

Implementar resolutores de tipos com foco na segurança

Mesmo com BinaryFormatter habilitado, você deve implementar resolvedores de tipos para restringir a desserialização a tipos explicitamente aprovados. Siga estas diretrizes:

  • Use listas de permissões explícitas. Rejeite qualquer tipo não aprovado explicitamente.
  • Validar nomes de tipo. Verifique se os nomes de tipo correspondem exatamente aos valores esperados.
  • Limitar a tipos essenciais. Inclua apenas os tipos necessários para a funcionalidade da área de transferência.
  • Gerar exceções para tipos desconhecidos. Rejeite claramente tipos não autorizados.
  • Examine regularmente. Audite e atualize a lista permitida conforme necessário.

Para obter exemplos de implementação completos e exemplos de código, consulte Implementar resolvedores de tipo focados em segurança no guia de instruções.

Usar a IA para migrar o código da área de transferência

A migração de operações de área de transferência do .NET 8 para o .NET 10 envolve alterações sistemáticas de código em vários arquivos e classes. Ferramentas de IA como o GitHub Copilot podem ajudar a acelerar sua migração identificando padrões herdados, sugerindo substituições modernas e criando cenários de teste. Em vez de pesquisar manualmente em sua base de código e converter cada operação de área de transferência individualmente, você pode usar IA para lidar com tarefas repetitivas enquanto se concentra em validar os resultados e lidar com casos extremos.

As seções a seguir mostram estratégias de prompt específicas para diferentes aspectos da migração da área de transferência, desde a localização de padrões de código problemáticos até a criação de tipos serializáveis JSON robustos e conjuntos de testes abrangentes.

Usar a IA para identificar padrões de área de transferência herdados

Use Copilot para verificar sua base de código e localizar operações de área de transferência que precisam de migração. Isso ajuda você a entender o escopo das alterações necessárias antes de iniciar o trabalho de migração real.

Find all clipboard operations in my codebase that use GetData(), SetData() with custom objects, DataObject.GetData(), or IDataObject.GetData(). Show me the file paths and line numbers where these patterns occur.

Copilot é alimentado pela IA, portanto, surpresas e erros são possíveis. Para obter mais informações, consulte perguntas frequentessobre uso geral do Copilot.

Usar IA para converter GetData() em TryGetData<T>()

Use o Copilot para converter chamadas obsoletas GetData() para o novo padrão TryGetData<T>() de tipo seguro. Essa conversão inclui o tratamento de erros adequado e elimina o casting inseguro.

Convert this GetData() clipboard code to use the new TryGetData<T>() method with proper error handling:

[paste your existing GetData() code here]

Make sure to eliminate casting and add appropriate error handling for when the data isn't available.

Copilot é alimentado pela IA, portanto, surpresas e erros são possíveis. Para obter mais informações, consulte perguntas frequentessobre uso geral do Copilot.

Usar a IA para migrar SetData() para SetDataAsJson<T>()

Use Copilot para converter o armazenamento de objetos personalizados do método obsoleto SetData() para a nova SetDataAsJson<T>() abordagem. Isso garante que seus objetos personalizados sejam serializados corretamente na área de transferência.

Take this SetData() clipboard code that stores custom objects:

[paste your existing SetData() code here]

Convert it to use SetDataAsJson<T>() and make the custom types JSON-serializable. Add any necessary System.Text.Json attributes if the types have complex properties.

Copilot é alimentado pela IA, portanto, surpresas e erros são possíveis. Para obter mais informações, consulte perguntas frequentessobre uso geral do Copilot.

Usar a IA para criar modelos de dados serializáveis em JSON

Use o Copilot para projetar tipos personalizados que funcionam perfeitamente com SetDataAsJson<T>() e TryGetData<T>(). Isso inclui a adição de atributos apropriados para propriedades que precisam de tratamento especial.

Create a JSON-serializable version of this class for clipboard operations:

[paste your existing class definition here]

Make it work with System.Text.Json, add JsonIgnore for sensitive properties, JsonInclude for private fields that should serialize, and JsonPropertyName for any properties that need different names in JSON.

Copilot é alimentado pela IA, portanto, surpresas e erros são possíveis. Para obter mais informações, consulte perguntas frequentessobre uso geral do Copilot.

Usar a IA para gerar métodos de wrapper que sejam de tipo seguro

Use o Copilot para criar métodos de encapsulamento que encapsulam as novas APIs de clipboard e fornecem interfaces limpas para os tipos de dados específicos do aplicativo.

Create a type-safe clipboard wrapper class that provides methods for storing and retrieving these custom types:

[list your custom types here]

Use SetDataAsJson<T>() and TryGetData<T>() internally, include proper error handling, and add methods like SavePersonToClipboard() and TryGetPersonFromClipboard().

Copilot é alimentado pela IA, portanto, surpresas e erros são possíveis. Para obter mais informações, consulte perguntas frequentessobre uso geral do Copilot.

Usar a IA para criar testes abrangentes

Use o Copilot para gerar conjuntos de testes que verificam se a migração da área de transferência funciona corretamente, incluindo testes de serialização de ida e volta e cenários de tratamento de erros.

Generate comprehensive unit tests for this clipboard code:

[paste your migrated clipboard code here]

Include tests for successful round-trip serialization, handling of null values, error cases when data isn't available, and verification that the migrated code produces the same results as the original for valid scenarios.

Copilot é alimentado pela IA, portanto, surpresas e erros são possíveis. Para obter mais informações, consulte perguntas frequentessobre uso geral do Copilot.

Usar a IA para validar os resultados da migração

Use o Copilot para examinar o código migrado e identificar possíveis problemas ou áreas em que a migração pode não estar concluída.

Review this migrated clipboard code for potential issues:

[paste your migrated code here]

Check for: missing error handling, types that might not serialize properly to JSON, performance concerns with large objects, security issues, and any remaining uses of obsolete methods.

Copilot é alimentado pela IA, portanto, surpresas e erros são possíveis. Para obter mais informações, consulte perguntas frequentessobre uso geral do Copilot.