روش درک مخفی کردن و ظهور در C#: یک مشکل در دنیای واقعی حل شد

من به تازگی در کار خود با مشکلی روبرو شده ام که بسته به رابط ها ، در اطراف فرهنگ لغت های پرجمعیت می چرخید ، اما یکی از انواع مشتق شده در مجموعه مورد انتظار نشان داده نشده است. بعد از حفر عمیق تر ، من به مفهومی رسیدم که برای من جدید بود: روش مخفی کردنبشر این باعث می شد کلاس مشتق من برای رابط نامناسب باشد. من با روش اصلی آشنا بودم ، اما قبلاً هرگز با روش مخفی کردن روبرو نشده بودم.
در این مقاله ، من مشکلی را که با آن روبرو شدم ، چگونه آن را حل کردم و مفاهیم و تفاوت های بین مخفی کردن روش و روش مهم را توضیح خواهم داد.
مشکل
من در حال کار بر روی سیستمی بودم که کارتها توسط کلاسهای اجرا نشان داده می شدند ICard
رابط کارت ها به طور معمول پیاده سازی می کنند ICard
به طور غیر مستقیم از طریق رابط های دیگر ، مانند ICardWithHighlight
وت ICardWithRelatedCards
، که گسترش می یابد ICard
بشر
public interface ICard
{
string GetCardId();
}
public interface ICardWithHighlight : ICard
{
HighlightColor ShouldHighlight(Card card);
}
public interface ICardWithRelatedCards : ICard
{
bool ShouldShowForOpponent(Player opponent);
List<Card?> GetRelatedCards(Player player);
}
سیستم استفاده می کند بازتاب برای بارگیری پویا تمام کلاس های کارت و بررسی اینکه کدام رابط را اجرا می کنند. هدف این است که فرهنگ لغت ها را با نمونه های کارت جمع کنید ، که توسط منحصر به فرد آنها مشخص شده است CardId
بشر
کارت Arcanologist
دارای 2 شناسه مختلف است. بنابراین ، دارای 2 کلاس کارت است: Arcanologist
وت ArcanologistCore
، هر اجرا GetCardId()
با ارزش مربوطه از آنجا که آنها به همان کارت مراجعه می کنند ، من از وراثت استفاده کردم تا آنها ShouldHighlight()
روش در هر دو کلاس سازگار است. با این حال ، من با یک مشکل روبرو شدم: کلاس مشتق شده ArcanologistCore
به HighlightCards
فرهنگ لغت ، حتی اگر به نظر می رسید یک اجرای معتبر از آن است ICardWithHighlight
بشر
public class RelatedCardsManager
{
private Dictionary<string, ICardWithRelatedCards>? _relatedCards;
private Dictionary<string, ICardWithHighlight>? _highlightCards;
public Dictionary<string, ICardWithRelatedCards> RelatedCards => _relatedCards ??= InitializeRelatedCards();
public Dictionary<string, ICardWithHighlight> HighlightCards => _highlightCards ??= InitializeHighlightCards();
private Dictionary<string, ICardWithRelatedCards> InitializeRelatedCards()
{
var (relatedCardsDict, highlightCardsDict ) = InitializeCards();
_highlightCards = highlightCardsDict;
return relatedCardsDict;
}
private Dictionary<string, ICardWithHighlight> InitializeHighlightCards()
{
var (relatedCardsDict, highlightCardsDict ) = InitializeCards();
_relatedCards = relatedCardsDict;
return highlightCardsDict;
}
private (Dictionary<string, ICardWithRelatedCards>, Dictionary<string, ICardWithHighlight>) InitializeCards()
{
var cards = Assembly.GetAssembly(typeof(ICard)).GetTypes()
.Where(t => t.IsClass && !t.IsAbstract && typeof(ICard).IsAssignableFrom(t));
var relatedCardsDict = new Dictionary<string, ICardWithRelatedCards>();
var highlightCardsDict = new Dictionary<string, ICardWithHighlight>();
foreach(var card in cards)
{
var cardInstance = Activator.CreateInstance(card) as ICard;
if(cardInstance is ICardWithRelatedCards relatedCard)
{
relatedCardsDict[relatedCard.GetCardId()] = relatedCard;
}
if(cardInstance is ICardWithHighlight highlightCard)
{
highlightCardsDict[highlightCard.GetCardId()] = highlightCard;
}
}
return (relatedCardsDict, highlightCardsDict);
}
}
در کد بالا: – بازتاب برای بارگیری انواع موجود در مونتاژ که پیاده سازی می شود استفاده می شود ICard
رابط – ما بررسی می کنیم که آیا نوع پیاده سازی را اجرا می کند ICardWithHighlight
یا ICardWithRelatedCards
برای اضافه کردن آن به فرهنگ لغت مناسب. – تنبلی فرهنگ لغت ها را آغاز کنید ، و هنگامی که یک نفر اولیه می شود ، دیگری را نیز آغاز می کند.
حالا ، بیایید نگاهی به Arcanologist
وت ArcanologistCore
کلاس ها:
public class Arcanologist : ICardWithHighlight
{
public string GetCardId() => HearthDb.CardIds.Collectible.Mage.Arcanologist;
public HighlightColor ShouldHighlight(Card card) =>
HighlightColorHelper.GetHighlightColor(card.GetTag(GameTag.SECRET) > 0);
}
public class ArcanologistCore : Arcanologist
{
public new string GetCardId() => HearthDb.CardIds.Collectible.Mage.ArcanologistCore;
}
موضوع
مشکل هنگامی رخ داد که ArcanologistCore
در HighlightCards
فرهنگ لغت حتی ArcanologistCore
به درستی اجرا شده است ICardWithHighlight
و خودش را داشت GetCardId()
روش ، هنگام جمع آوری فرهنگ لغت هنوز نادیده گرفته می شد.
اولین راه حل من اضافه کردن دستی بود ICardWithHighlight
به ArcanologistCore
کلاس. این کار کرد و کارت در فرهنگ لغت ظاهر شد.
public class ArcanologistCore : Arcanologist, ICardWithHighlight
{
public new string GetCardId() => HearthDb.CardIds.Collectible.Mage.ArcanologistCore;
}
اما من از این راه حل راضی نبودم. من همیشه می دانستم که در OOP وقتی کلاس دیگری را گسترش می دهد ، باید تمام رابط های آن را اجرا کند ، بنابراین چرا باید صریحاً بنویسم که کلاس مشتق شده من در حال اجرای آن رابط است؟ بنابراین من شروع به پاسخ به این کار می کنم و آن را پیدا کردم. نامیده می شود روش مخفی کردنبشر
پنهان کردن روش و روش مهم چیست؟
در C#، روش مخفی کردن هنگامی اتفاق می افتد که یک کلاس مشتق شده روشی را با همان نام به عنوان یک روش در کلاس پایه تعریف کند ، اما بدون استفاده از آن override
کلمه کلیدی این باعث می شود روش کلاس مشتق شده روش کلاس پایه را پنهان کند. هنگام استفاده از روش مخفی کردن روش ، روش کلاس پایه خوانده نمی شود – در عوض ، از روش در کلاس مشتق شده فقط در صورتی استفاده می شود که مرجع به طور خاص از نوع مشتق شده باشد. این روش ، از آنجا GetCardId()
در کلاس پایه پنهان بود ، ArcanologistCore
دیگر به طور کامل اجرا نشده است ICard
رابط ، و در نتیجه ، آن را اجرا نکرد ICardWithHighlight
یا
روش مهماز طرف دیگر ، هنگامی اتفاق می افتد که یک کلاس مشتق شده از کلمه کلیدی Override استفاده کند تا اجرای خود را از یک روش از کلاس پایه فراهم کند. این تضمین می کند که روش در کلاس مشتق شده فراخوانی می شود ، حتی اگر مرجع از نوع کلاس پایه باشد.
راه حل
راه حل ساده بود: به جای استفاده از new
کلمه کلیدی در GetCardId()
در ArcanologistCore
، من نیاز به استفاده داشتم override
برای اینکه به درستی روش را از کلاس پایه نادیده بگیرید. این تضمین می کند که وقتی یک شی از نوع است ArcanologistCore
به عنوان یک ارجاع شده است ICardWithHighlight
، روش بیش از حد نامیده می شود و اجازه می دهد کارت به فرهنگ لغت اضافه شود. من همچنین مجبور شدم روش کلاس پایه را به عنوان علامت گذاری کنم virtual
برای گفتن C# که این روش می تواند نادیده گرفته شود.
در اینجا اجرای اصلاح شده است:
public class Arcanologist : ICardWithHighlight
{
public virtual string GetCardId() => HearthDb.CardIds.Collectible.Mage.Arcanologist;
public HighlightColor ShouldHighlight(Card card) =>
HighlightColorHelper.GetHighlightColor(card.GetTag(GameTag.SECRET) > 0);
}
public class ArcanologistCore : Arcanologist
{
public override string GetCardId() => HearthDb.CardIds.Collectible.Mage.ArcanologistCore;
}
پایان
بعد از تغییر new
کلمه کلیدی به override
در کلاس مشتق شده ، کارت به درستی اضافه شد HighlightCards
فرهنگ لغت ، و مسئله حل شد. این تجربه به من کمک کرد تا تفاوت بین آن را درک کنم روش مخفی کردن وت روش مهم در C#.