Skip to content

Bits of .NET

Daily micro-tips for C#, SQL, performance, and scalable backend engineering.

  • Asp.Net Core
  • C#
  • SQL
  • JavaScript
  • CSS
  • About
  • ErcanOPAK.com
  • No Access
  • Privacy Policy
C#

C#: Why Array Covariance Lets You Assign the Wrong Element Type Until It Throws at Runtime

- 04.10.26 - ErcanOPAK

🔧 The Array That Let You Write the Wrong Type In, Just Not Keep It

C# arrays are covariant for reference types, which means a string array can be assigned to an object array variable without any cast, because every string genuinely is an object. The compiler allows this assignment and then has no further opinion about what you store through that object array reference – it will happily let you write code that attempts to put an int or a completely unrelated reference type into what is, underneath, still a string array. The check that actually enforces the real element type doesn’t happen at compile time at all; it happens on every single array store, at runtime, in the form of an ArrayTypeMismatchException.

🔎 The Problem

string[] names = { "Ada", "Grace" };

// Compiles fine: string[] is assignable to object[] (covariance)
object[] items = names;

items[0] = "Linus"; // fine - still a string

items[1] = 42; // compiles! object[] can legally hold an int...

// ...but blows up at runtime, because the ARRAY is still really a
// string[] underneath, and the CLR checks the real element type on
// every store through a covariant array reference:
//   System.ArrayTypeMismatchException: Attempted to access an element
//   as a type incompatible with the array.

void Fill(object[] target, object value)
{
    for (int i = 0; i < target.Length; i++)
        target[i] = value; // generic-looking code, same hidden risk
}

Fill(names, 42); // compiles cleanly, throws only when actually called

✅ Fix: Don’t Rely on Array Covariance When the Element Type Actually Matters

  • Prefer a read-only generic sequence type (IReadOnlyList or IEnumerable) for parameters that only need to read elements – covariant interfaces give you the same flexibility without ever exposing a write path that can fail.
  • If a method genuinely needs to write into a caller-supplied array, declare the parameter as the specific array type you need (string[], not object[]) so a type mismatch becomes a compile error instead of a runtime exception.
  • When you must accept object[] for a generic-looking utility, document that it assumes the caller passed an actual object[] (or treat any write as something that can throw) rather than assuming covariance makes it safe.

⚠️ Why This Is Easy to Miss

  • The code compiles without a single warning, because covariant array assignment is a deliberately legal C# feature – nothing in the type system flags the later write as suspicious.
  • The failure only appears on the specific line that writes an incompatible element, which can be buried deep inside a generic-looking helper method that was written and tested only against its exact declared type.

Array covariance lets the compiler trust you with a promise the runtime was never willing to make – and the runtime always gets the last word.

— C# Best Practice

Related posts:

C#: Fix a StringBuilder That's Slower Than Plain String Concatenation in a Tight Loop

C#: Use Span for High-Performance Memory Access Without Allocation

What is the difference between IQueryable and IEnumerable?

Post Views: 1

Post navigation

ASP.NET Core: Why Combining [FromBody] and [FromForm] Parameters on One Action Throws at Startup Instead of Binding Both
WordPress: Fix Featured Images That Disappear From RSS Feeds While Still Showing Fine on the Site

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

October 2026
M T W T F S S
 1234
567891011
12131415161718
19202122232425
262728293031  
« Sep    

Most Viewed Posts

  • Get the User Name and Domain Name from an Email Address in SQL (973)
  • How to make theater mode the default for Youtube (948)
  • How to add default value for Entity Framework migrations for DateTime and Bool (938)
  • Get the First and Last Word from a String or Sentence in SQL (847)
  • How to select distinct rows in a datatable in C# (836)
  • How to enable, disable and check if Service Broker is enabled on a database in SQL Server (624)
  • Add Constraint to SQL Table to ensure email contains @ (590)
  • Average of all values in a column that are not zero in SQL (553)
  • How to use Map Mode for Vertical Scroll Mode in Visual Studio (526)
  • Find numbers with more than two decimal places in SQL (468)

Recent Posts

  • CSS: Fix a prefers-color-scheme Media Query That Gets Silently Overridden by a Browser Extension’s Forced Dark Mode
  • Git: Fix a Merge Commit That Silently Drops a File Because Both Branches Deleted It Differently
  • HTML5: Fix a Native Lazy-Loading Image That Never Loads Because It Sits Inside a Hidden Tab Until the User Clicks It
  • The AI Prompt That Traces a Null Reference Exception Back to the Exact Line That First Produced the Null
  • The AI Prompt That Turns a Gym Membership Contract’s Fine Print Into a Plain-English List of Cancellation Steps
  • Photoshop: Fix a Color Profile Mismatch That Makes Printed Output Look Nothing Like What You Saw On Screen
  • WordPress: Fix Search Results That Return Pages From a Theme You Deactivated Months Ago
  • Visual Studio: Fix a Test Project That Builds Fine Alone but Fails to Discover Any Tests After a NuGet Restore
  • ASP.NET Core: Fix a File Upload That Times Out on Slow Connections Only Because Kestrel’s Minimum Data Rate Feature Kicked In
  • JavaScript: Fix an Array Destructuring Default Value That Silently Never Applies Because null Was Passed Instead of Undefined

Most Viewed Posts

  • Get the User Name and Domain Name from an Email Address in SQL (973)
  • How to make theater mode the default for Youtube (948)
  • How to add default value for Entity Framework migrations for DateTime and Bool (938)
  • Get the First and Last Word from a String or Sentence in SQL (847)
  • How to select distinct rows in a datatable in C# (836)

Recent Posts

  • CSS: Fix a prefers-color-scheme Media Query That Gets Silently Overridden by a Browser Extension’s Forced Dark Mode
  • Git: Fix a Merge Commit That Silently Drops a File Because Both Branches Deleted It Differently
  • HTML5: Fix a Native Lazy-Loading Image That Never Loads Because It Sits Inside a Hidden Tab Until the User Clicks It
  • The AI Prompt That Traces a Null Reference Exception Back to the Exact Line That First Produced the Null
  • The AI Prompt That Turns a Gym Membership Contract’s Fine Print Into a Plain-English List of Cancellation Steps

Social

  • ErcanOPAK.com
  • GoodReads
  • LetterBoxD
  • Linkedin
  • The Blog
  • Twitter
© 2026 Bits of .NET | Built with Xblog Plus free WordPress theme by wpthemespace.com