Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 1 addition & 40 deletions src/Kook.Net.Core/Entities/Messages/Cards/Card.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
using System.Collections.Immutable;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;

namespace Kook;

/// <summary>
/// 表示一个卡片对象,可用于卡片消息。
/// </summary>
[DebuggerDisplay("{DebuggerDisplay,nq}")]
public class Card : ICard, IEquatable<Card>, IEquatable<ICard>
public record Card : ICard
{
internal Card(CardTheme theme, CardSize size, Color? color, ImmutableArray<IModule> modules)
{
Expand Down Expand Up @@ -46,44 +45,6 @@ internal Card(CardTheme theme, CardSize size, Color? color, ImmutableArray<IModu

private string DebuggerDisplay => $"{Type} ({Modules.Length} Modules)";

/// <summary>
/// 判定两个 <see cref="Card"/> 是否相等。
/// </summary>
/// <returns> 如果两个 <see cref="Card"/> 相等,则为 <c>true</c>;否则为 <c>false</c>。 </returns>
public static bool operator ==(Card left, Card right) =>
left?.Equals(right) ?? right is null;

/// <summary>
/// 判定两个 <see cref="Card"/> 是否不相等。
/// </summary>
/// <returns> 如果两个 <see cref="Card"/> 不相等,则为 <c>true</c>;否则为 <c>false</c>。 </returns>
public static bool operator !=(Card left, Card right) =>
!(left == right);

/// <inheritdoc />
public override bool Equals([NotNullWhen(true)] object? obj) =>
obj is Card card && Equals(card);

/// <inheritdoc />
public bool Equals([NotNullWhen(true)] Card? card) =>
GetHashCode() == card?.GetHashCode();

/// <inheritdoc />
public override int GetHashCode()
{
unchecked
{
int hash = (int)2166136261;
hash = (hash * 16777619) ^ (Type, Theme, Color, Size).GetHashCode();
foreach (IModule module in Modules)
hash = (hash * 16777619) ^ module.GetHashCode();
return hash;
}
}

bool IEquatable<ICard>.Equals([NotNullWhen(true)] ICard? card) =>
Equals(card as Card);

/// <inheritdoc />
IReadOnlyCollection<IModule> ICard.Modules => Modules;
}
46 changes: 1 addition & 45 deletions src/Kook.Net.Core/Entities/Messages/Cards/CardBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace Kook;
/// <summary>
/// 用来构建 <see cref="Card"/> 卡片的构建器。
/// </summary>
public class CardBuilder : ICardBuilder, IEquatable<CardBuilder>, IEquatable<ICardBuilder>
public record CardBuilder : ICardBuilder

Copilot AI Feb 14, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Converting mutable builder classes to records is problematic. Builders have mutable properties (e.g., Theme, Color, Size, Modules) and use the fluent API pattern (returning this from methods). Records provide value-based equality which is inappropriate for mutable objects, as mutating a record can cause hash code instability and unexpected equality behavior. Builders should remain as classes since they are designed to be mutated during their lifetime.

Copilot uses AI. Check for mistakes.
{
/// <summary>
/// 初始化一个 <see cref="CardBuilder"/> 类的新实例。
Expand Down Expand Up @@ -152,48 +152,4 @@ or AudioModuleBuilder

/// <inheritdoc />
ICard ICardBuilder.Build() => Build();

/// <summary>
/// 判定两个 <see cref="CardBuilder"/> 是否相等。
/// </summary>
/// <returns> 如果两个 <see cref="CardBuilder"/> 相等,则为 <c>true</c>;否则为 <c>false</c>。 </returns>
public static bool operator ==(CardBuilder? left, CardBuilder? right) =>
left?.Equals(right) ?? right is null;

/// <summary>
/// 判定两个 <see cref="CardBuilder"/> 是否不相等。
/// </summary>
/// <returns> 如果两个 <see cref="CardBuilder"/> 不相等,则为 <c>true</c>;否则为 <c>false</c>。 </returns>
public static bool operator !=(CardBuilder? left, CardBuilder? right) =>
!(left == right);

/// <inheritdoc />
public override bool Equals([NotNullWhen(true)] object? obj) =>
obj is CardBuilder builder && Equals(builder);

/// <inheritdoc />
public bool Equals([NotNullWhen(true)] CardBuilder? cardBuilder)
{
if (cardBuilder is null)
return false;

if (Modules.Count != cardBuilder.Modules.Count)
return false;

if (Modules
.Zip(cardBuilder.Modules, (x, y) => (x, y))
.Any(pair => pair.x != pair.y))
return false;

return Type == cardBuilder.Type
&& Theme == cardBuilder.Theme
&& Color == cardBuilder.Color
&& Size == cardBuilder.Size;
}

/// <inheritdoc />
public override int GetHashCode() => base.GetHashCode();

bool IEquatable<ICardBuilder>.Equals([NotNullWhen(true)] ICardBuilder? cardBuilder) =>
Equals(cardBuilder as CardBuilder);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace Kook;
/// <summary>
/// 用来构建 <see cref="ButtonElement"/> 元素的构建器。
/// </summary>
public class ButtonElementBuilder : IElementBuilder, IEquatable<ButtonElementBuilder>, IEquatable<IElementBuilder>
public record ButtonElementBuilder : IElementBuilder

Copilot AI Feb 14, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Converting mutable builder classes to records is problematic. Builders have mutable properties and use the fluent API pattern (returning this from methods). Records provide value-based equality which is inappropriate for mutable objects. Builders should remain as classes since they are designed to be mutated during their lifetime.

Copilot uses AI. Check for mistakes.
{
/// <summary>
/// 按钮文本的最大长度。
Expand Down Expand Up @@ -233,41 +233,4 @@ public ButtonElement Build()
/// <inheritdoc />
[MemberNotNull(nameof(Text))]
IElement IElementBuilder.Build() => Build();

/// <summary>
/// 判定两个 <see cref="ButtonElementBuilder"/> 是否相等。
/// </summary>
/// <returns> 如果两个 <see cref="ButtonElementBuilder"/> 相等,则为 <c>true</c>;否则为 <c>false</c>。 </returns>
public static bool operator ==(ButtonElementBuilder? left, ButtonElementBuilder? right) =>
left?.Equals(right) ?? right is null;

/// <summary>
/// 判定两个 <see cref="ButtonElementBuilder"/> 是否不相等。
/// </summary>
/// <returns> 如果两个 <see cref="ButtonElementBuilder"/> 不相等,则为 <c>true</c>;否则为 <c>false</c>。 </returns>
public static bool operator !=(ButtonElementBuilder? left, ButtonElementBuilder? right) =>
!(left == right);

/// <inheritdoc />
public override bool Equals([NotNullWhen(true)] object? obj) =>
obj is ButtonElementBuilder builder && Equals(builder);

/// <inheritdoc />
public bool Equals([NotNullWhen(true)] ButtonElementBuilder? buttonElementBuilder)
{
if (buttonElementBuilder is null)
return false;

return Type == buttonElementBuilder.Type
&& Theme == buttonElementBuilder.Theme
&& Value == buttonElementBuilder.Value
&& Click == buttonElementBuilder.Click
&& Text == buttonElementBuilder.Text;
}

/// <inheritdoc />
public override int GetHashCode() => base.GetHashCode();

bool IEquatable<IElementBuilder>.Equals([NotNullWhen(true)] IElementBuilder? elementBuilder) =>
Equals(elementBuilder as ButtonElementBuilder);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace Kook;
/// <summary>
/// 用来构建 <see cref="ImageElement"/> 元素的构建器。
/// </summary>
public class ImageElementBuilder : IElementBuilder, IEquatable<ImageElementBuilder>, IEquatable<IElementBuilder>
public record ImageElementBuilder : IElementBuilder

Copilot AI Feb 14, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Converting mutable builder classes to records is problematic. Builders have mutable properties and use the fluent API pattern (returning this from methods). Records provide value-based equality which is inappropriate for mutable objects. Builders should remain as classes since they are designed to be mutated during their lifetime.

Suggested change
public record ImageElementBuilder : IElementBuilder
public class ImageElementBuilder : IElementBuilder

Copilot uses AI. Check for mistakes.
{
/// <summary>
/// 图片替代文本的最大长度。
Expand Down Expand Up @@ -193,41 +193,4 @@ public ImageElement Build()
/// <inheritdoc />
[MemberNotNull(nameof(Source))]
IElement IElementBuilder.Build() => Build();

/// <summary>
/// 判定两个 <see cref="ImageElementBuilder"/> 是否相等。
/// </summary>
/// <returns> 如果两个 <see cref="ImageElementBuilder"/> 相等,则为 <c>true</c>;否则为 <c>false</c>。 </returns>
public static bool operator ==(ImageElementBuilder? left, ImageElementBuilder? right) =>
left?.Equals(right) ?? right is null;

/// <summary>
/// 判定两个 <see cref="ImageElementBuilder"/> 是否不相等。
/// </summary>
/// <returns> 如果两个 <see cref="ImageElementBuilder"/> 不相等,则为 <c>true</c>;否则为 <c>false</c>。 </returns>
public static bool operator !=(ImageElementBuilder? left, ImageElementBuilder? right) =>
!(left == right);

/// <inheritdoc />
public override bool Equals([NotNullWhen(true)] object? obj) =>
obj is ImageElementBuilder builder && Equals(builder);

/// <inheritdoc />
public bool Equals([NotNullWhen(true)] ImageElementBuilder? imageElementBuilder)
{
if (imageElementBuilder is null) return false;

return Type == imageElementBuilder.Type
&& Source == imageElementBuilder.Source
&& Alternative == imageElementBuilder.Alternative
&& Size == imageElementBuilder.Size
&& Circle == imageElementBuilder.Circle
&& FallbackUrl == imageElementBuilder.FallbackUrl;
}

/// <inheritdoc />
public override int GetHashCode() => base.GetHashCode();

bool IEquatable<IElementBuilder>.Equals([NotNullWhen(true)] IElementBuilder? elementBuilder) =>
Equals(elementBuilder as ImageElementBuilder);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace Kook;
/// <summary>
/// 用来构建 <see cref="KMarkdownElement"/> 元素的构建器。
/// </summary>
public class KMarkdownElementBuilder : IElementBuilder, IEquatable<KMarkdownElementBuilder>, IEquatable<IElementBuilder>
public record KMarkdownElementBuilder : IElementBuilder

Copilot AI Feb 14, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Converting mutable builder classes to records is problematic. Builders have mutable properties and use the fluent API pattern (returning this from methods). Records provide value-based equality which is inappropriate for mutable objects. Builders should remain as classes since they are designed to be mutated during their lifetime.

Suggested change
public record KMarkdownElementBuilder : IElementBuilder
public class KMarkdownElementBuilder : IElementBuilder

Copilot uses AI. Check for mistakes.
{
/// <summary>
/// KMarkdown 文本的最大长度。
Expand Down Expand Up @@ -81,37 +81,4 @@ public KMarkdownElement Build()
/// <inheritdoc />
[MemberNotNull(nameof(Content))]
IElement IElementBuilder.Build() => Build();

/// <summary>
/// 判定两个 <see cref="KMarkdownElementBuilder"/> 是否相等。
/// </summary>
/// <returns> 如果两个 <see cref="KMarkdownElementBuilder"/> 相等,则为 <c>true</c>;否则为 <c>false</c>。 </returns>
public static bool operator ==(KMarkdownElementBuilder? left, KMarkdownElementBuilder? right) =>
left?.Equals(right) ?? right is null;

/// <summary>
/// 判定两个 <see cref="KMarkdownElementBuilder"/> 是否不相等。
/// </summary>
/// <returns> 如果两个 <see cref="KMarkdownElementBuilder"/> 不相等,则为 <c>true</c>;否则为 <c>false</c>。 </returns>
public static bool operator !=(KMarkdownElementBuilder? left, KMarkdownElementBuilder? right) =>
!(left == right);

/// <inheritdoc />
public override bool Equals([NotNullWhen(true)] object? obj) =>
obj is KMarkdownElementBuilder builder && Equals(builder);

/// <inheritdoc />
public bool Equals([NotNullWhen(true)] KMarkdownElementBuilder? kMarkdownElementBuilder)
{
if (kMarkdownElementBuilder is null)
return false;
return Type == kMarkdownElementBuilder.Type
&& Content == kMarkdownElementBuilder.Content;
}

/// <inheritdoc />
public override int GetHashCode() => base.GetHashCode();

bool IEquatable<IElementBuilder>.Equals([NotNullWhen(true)] IElementBuilder? elementBuilder) =>
Equals(elementBuilder as KMarkdownElementBuilder);
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
using System.Diagnostics.CodeAnalysis;

namespace Kook;

/// <summary>
/// 用来构建 <see cref="ParagraphStruct"/> 元素的构建器。
/// </summary>
public class ParagraphStructBuilder : IElementBuilder, IEquatable<ParagraphStructBuilder>, IEquatable<IElementBuilder>
public record ParagraphStructBuilder : IElementBuilder

Copilot AI Feb 14, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Converting mutable builder classes to records is problematic. Builders have mutable properties and use the fluent API pattern (returning this from methods). Records provide value-based equality which is inappropriate for mutable objects. Builders should remain as classes since they are designed to be mutated during their lifetime.

Suggested change
public record ParagraphStructBuilder : IElementBuilder
public class ParagraphStructBuilder : IElementBuilder

Copilot uses AI. Check for mistakes.
{
/// <summary>
/// 区域文本内文本块的最大数量。
Expand Down Expand Up @@ -165,46 +163,4 @@ public ParagraphStruct Build()

/// <inheritdoc />
IElement IElementBuilder.Build() => Build();

/// <summary>
/// 判定两个 <see cref="ParagraphStructBuilder"/> 是否相等。
/// </summary>
/// <returns> 如果两个 <see cref="ParagraphStructBuilder"/> 相等,则为 <c>true</c>;否则为 <c>false</c>。 </returns>
public static bool operator ==(ParagraphStructBuilder? left, ParagraphStructBuilder? right) =>
left?.Equals(right) ?? right is null;

/// <summary>
/// 判定两个 <see cref="ParagraphStructBuilder"/> 是否不相等。
/// </summary>
/// <returns> 如果两个 <see cref="ParagraphStructBuilder"/> 不相等,则为 <c>true</c>;否则为 <c>false</c>。 </returns>
public static bool operator !=(ParagraphStructBuilder? left, ParagraphStructBuilder? right) =>
!(left == right);

/// <inheritdoc />
public override bool Equals([NotNullWhen(true)] object? obj) =>
obj is ParagraphStructBuilder builder && Equals(builder);

/// <inheritdoc />
public bool Equals([NotNullWhen(true)] ParagraphStructBuilder? paragraphStructBuilder)
{
if (paragraphStructBuilder is null)
return false;

if (Fields.Count != paragraphStructBuilder.Fields.Count)
return false;

if (Fields
.Zip(paragraphStructBuilder.Fields, (x, y) => (x, y))
.Any(pair => pair.x != pair.y))
return false;

return Type == paragraphStructBuilder.Type
&& ColumnCount == paragraphStructBuilder.ColumnCount;
}

/// <inheritdoc />
public override int GetHashCode() => base.GetHashCode();

bool IEquatable<IElementBuilder>.Equals([NotNullWhen(true)] IElementBuilder? elementBuilder) =>
Equals(elementBuilder as ParagraphStructBuilder);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace Kook;
/// <summary>
/// 用来构建 <see cref="PlainTextElement"/> 元素的构建器。
/// </summary>
public class PlainTextElementBuilder : IElementBuilder, IEquatable<PlainTextElementBuilder>, IEquatable<IElementBuilder>
public class PlainTextElementBuilder : IElementBuilder
{
/// <summary>
/// 纯文本的最大长度。
Expand Down Expand Up @@ -103,38 +103,4 @@ public PlainTextElement Build()
/// <inheritdoc />
[MemberNotNull(nameof(Content))]
IElement IElementBuilder.Build() => Build();

/// <summary>
/// 判定两个 <see cref="PlainTextElementBuilder"/> 是否相等。
/// </summary>
/// <returns> 如果两个 <see cref="PlainTextElementBuilder"/> 相等,则为 <c>true</c>;否则为 <c>false</c>。 </returns>
public static bool operator ==(PlainTextElementBuilder? left, PlainTextElementBuilder? right) =>
left?.Equals(right) ?? right is null;

/// <summary>
/// 判定两个 <see cref="PlainTextElementBuilder"/> 是否不相等。
/// </summary>
/// <returns> 如果两个 <see cref="PlainTextElementBuilder"/> 不相等,则为 <c>true</c>;否则为 <c>false</c>。 </returns>
public static bool operator !=(PlainTextElementBuilder? left, PlainTextElementBuilder? right) =>
!(left == right);

/// <inheritdoc />
public override bool Equals([NotNullWhen(true)] object? obj) =>
obj is PlainTextElementBuilder builder && Equals(builder);

/// <inheritdoc />
public bool Equals([NotNullWhen(true)] PlainTextElementBuilder? plainTextElementBuilder)
{
if (plainTextElementBuilder is null) return false;

return Type == plainTextElementBuilder.Type
&& Content == plainTextElementBuilder.Content
&& Emoji == plainTextElementBuilder.Emoji;
}

/// <inheritdoc />
public override int GetHashCode() => base.GetHashCode();

bool IEquatable<IElementBuilder>.Equals([NotNullWhen(true)] IElementBuilder? elementBuilder) =>
Equals(elementBuilder as PlainTextElementBuilder);
}
Loading