واکاوی تفاوتهای طراحی در سیشارپ و جاوا
نویسنده: وحید نصیری
تاریخ: ۱۴۰۵/۰۶/۲۲ ۱۰:۰۵
آدرس: www.dntips.ir
چکیده: مقایسه زبانهای برنامهنویسی هنگامی ارزش علمی و کاربردی مییابد که فراتر از رقابتهای سطحی، به ریشهیابی تصمیمات معماری ماشینهای مجازی و سیستم نوعها (Type Systems) بپردازد. دو اکوسیستم جاوا (JVM) و سیشارپ (.NET/CLR) با وجود ریشههای مشترک نحوی در خانواده C، در نحوه انتزاع و پاسخ به مسائل بنیادینی نظیر انقیاد نوعها، رفتار انواع مقداری در برابر ارجاعی، مدل فرستادن فراخوانیها (Delegation) و محدودیتهای رابطها (Interfaces) مسیرهای متفاوتی را پیمودهاند.
enum اساساً نوعی مقداردار (Value Type) با یک نوع پایهای عددی (به طور پیشفرض int) است. این ماهیت، عملیات مبتنی بر بیت (Bitwise Operations) و حافظهبندی فشرده را بسیار کمهزینه و سریع میکند؛ اما امکان تعریف رفتار، متد، یا سازنده را درون خود نوع شمارشی سلب مینماید.enum در جاوا یک ساختار کلاسمحور است که از کلاس پایهای java.lang.Enum ارثبری میکند. در واقع در جاوا، اینام، نوعی سینتکس روان برای ایجاد یک نوع ارجاعی (Reference Type) با نمونههای تکین (Singleton) ازپیشتعریفشده است.public interface ToPattern {
Pattern getPattern();
}
public enum TokenType implements ToPattern {
IDENTIFIER("[a-zA-Z][a-zA-Z0-9]*"),
NUMBER("[0-9]+"),
ASSIGN("[=]");
private final String pattern;
TokenType(String pattern) {
this.pattern = pattern;
}
@Override
public Pattern getPattern() {
return Pattern.compile(this.pattern);
}
}[AttributeUsage(AttributeTargets.Field)]
public sealed class PatternAttribute(string pattern) : Attribute
{
public string Pattern { get; } = pattern;
}
public enum TokenType
{
[Pattern("[a-zA-Z][a-zA-Z0-9]*")]
Identifier,
[Pattern("[0-9]+")]
Number,
[Pattern("[=]")]
Assign
}
public static class TokenTypeExtensions
{
public static Regex GetRegex(this TokenType tokenType)
{
var memberInfo = typeof(TokenType).GetMember(tokenType.ToString());
var attribute = memberInfo[0].GetCustomAttribute<PatternAttribute>();
return new Regex(attribute?.Pattern ?? string.Empty);
}
}public interface IHasRegex
{
Regex Regex { get; }
}
public abstract class TokenType : IHasRegex
{
public static readonly TokenType Identifier = new IdentifierType();
public static readonly TokenType Number = new NumberType();
public static readonly TokenType Assign = new AssignType();
public abstract Regex Regex { get; }
private TokenType() { } // سازنده خصوصی برای جلوگیری از توسعه کنترلنشده خارجی
private sealed class IdentifierType : TokenType
{
public override Regex Regex { get; } = new("[a-zA-Z][a-zA-Z0-9]*", RegexOptions.Compiled);
}
private sealed class NumberType : TokenType
{
public override Regex Regex { get; } = new("[0-9]+", RegexOptions.Compiled);
}
private sealed class AssignType : TokenType
{
public override Regex Regex { get; } = new("[=]", RegexOptions.Compiled);
}
}نکته تکمیلی برای توسعهدهندگان داتنت:
با معرفی قابلیتهای پیشرفته الگوخوانی (Pattern Matching) در نسخههای مدرن سیشارپ (C# 9 تا C# 12+)، ترکیب کلاسهای مهرومومشده یا سلسلهمراتب کلاسهای پایه و تطبیق ساختار (Pattern Matching over Type hierarchies) میتواند بهمراتب تمیزتر ازswitchهای مبتنی بر اعداد رفتار کند. همچنین کتابخانههای محبوبی نظیرArdalis.SmartEnumاستاندارد صنعتی این الگو در اکوسیستم داتنت هستند.
public abstract record Fruit;
public record Apple : Fruit;
public abstract class FruitFactory
{
public abstract Fruit Create();
}
public class AppleFactory : FruitFactory
{
// هموردایی در سطح کلاسها: کامپایلر و CLR اجازه تغییر Fruit به Apple را میدهند
public override Apple Create() => new();
}// در کتابخانه استاندارد داتنت، ICloneable اینگونه تعریف شده:
// public object Clone();
public class Document : ICloneable
{
// خطای کامپایل:
// Return type should be 'object' to match implemented interface member
public Document Clone() => new();
}public class Document : ICloneable
{
// متد عمومی با نوع مشخص بدون نیاز به Cast از سمت فراخوان
public Document Clone() => new Document();
// پیادهسازی صریح اینترفیس برای حفظ سازگاری با متد ICloneable
object ICloneable.Clone() => this.Clone();
}::) مقداردهی شود:@FunctionalInterface
public interface BinaryOperation {
int apply(int a, int b);
}
// مقداردهی با ارجاع به متد یا لامبدا
BinaryOperation add = Integer::sum;
int result = add.apply(4, 6);System.MulticastDelegate هستند که ایمنی کامل انواع، قابلیت زنجیرهسازی چندگانه (Multicast) و پشتیبانی همهجانبه در رویدادها (Events) را دارند.// در سیشارپ نیازی به تعریف اینترفیس تکمتده نیست Func<int, int, int> add = (a, b) => a + b; int result = add(4, 6);
Action<...> و Func<...>.public interface KeyValuePair<K, V> {
K getKey();
V getValue();
}
// پیادهسازی بینام در جاوا:
var pair = new KeyValuePair<String, Integer>() {
@Override
public String getKey() { return "Score"; }
@Override
public Integer getValue() { return 100; }
};new { Key = "Score", Value = 100 } پشتیبانی میکند، اما این انواع نمیتوانند اینترفیسی را پیادهسازی کنند یا از کلاسی به غیر از System.Object ارثبری نمایند.public void ProcessData()
{
// تعریف کلاس درون متد به شکل محلی
class LocalPair : IKeyValuePair<string, int>
{
public string Key => "Score";
public int Value => 100;
}
IKeyValuePair<string, int> pair = new LocalPair();
// ...
}record).Integer a1 = 100, a2 = 100; System.out.println(a1 == a2); // خروجی: true Integer b1 = 200, b2 = 200; System.out.println(b1 == b2); // خروجی: false!
Integer و Double نوع ارجاعی هستند. در جاوا، عملگر == برای اشیای ارجاعی همواره تساوی آدرس حافظه (Reference Equality) را بررسی میکند و نه برابری محتوایی را.new Integer()) روی هیپ تخصیص مییابد و مقایسه ارجاعی شکست میخورد.در جاوا راهکار صحیح همواره استفاده از متدequals.است:b1.equals(b2)
int تنها یک نام مستعار برای ساختار مقداری System.Int32 است.int a1 = 100, a2 = 100; Console.WriteLine(a1 == a2); // True Int32 b1 = 200, b2 = 200; Console.WriteLine(b1 == b2); // True
int) در نهایت از System.Object ارث میبرند اما تا زمانی که صریحاً به شیء ارجاعی تبدیل نشوند، به عنوان ساختار مقداری روی پشته یا مستقیماً در بلاک حافظه والد قرار میگیرند.== برای ساختارهای استاندارد سربارگذاری شده و همواره برابری مقداری (Value Equality) را میسنجد.object کست شود. در مقایسه دو شیء از جنس object، رفتار تساوی ارجاعی به صورت شفاف اعمال خواهد شد و هیچ مقیاس شرطی یا کَش پنهانی وجود ندارد.| ویژگی | جاوا (JVM) | سیشارپ (.NET/CLR) | بینش مهندسی برای توسعهدهنده داتنت |
| نوعهای شمارشی (Enums) | کلاس کامل، پشتیبانی از متد، فیلد و اینترفیس | مقدار عددی (Value Type) مبتنی بر ثوابت صحیح | استفاده از الگوی Smart Enum برای کدهای نیازمند رفتار غنی |
| هموردایی نوع خروجی | پشتیبانی در کلاسها و رابطها | پشتیبانی در بازنویسی کلاسها؛ فاقد پشتیبانی مستقیم در رابطها | بهرهگیری از تکنیک Explicit Interface Implementation |
| برنامهنویسی تابعی | رابط تکمتده (@FunctionalInterface) | انواع اصیل درونساخت CLR به نام Delegate و ساختارهای عمومی نظیر Func / Action | معماری داتنت در این بخش کارآمدتر، سبکتر و صریحتر است |
| کلاسهای بینام برای رابطها | ساخت نمونههای ناشناس با پیادهسازی درجا | عدم پشتیبانی؛ پشتیبانی محدود به Anonymous Types دادهمحور | استفاده از Local Classes، رکوردهای مجزا، یا تزریق Delegateها |
| مدیریت نوعهای پایه و Boxing | تفاوت Primitives با Objectها، کَش پنهان اعداد ۱۲۸- تا ۱۲۷ | ساختارهای مقداری (Structs) اصیل، بازنویسی == برای مقادیر، رفتار پایدار | درک شفاف تفاوت انواع مقداری و ارجاعی بدون نگرانی از کَش پنهان |