Question
Consider the following C# event handler:
void Handler(object o, EventArgs e)
{
// Assume that o is intended to contain a string.
string s1 = (string)o;
// Or:
string? s2 = o as string;
// Or:
string s3 = o.ToString();
}
What is the difference between a direct cast, the as operator, and calling ToString()? Which approach should be preferred in different situations?
Short Answer
By the end of this page, you will know how C# converts a value stored as object into a string, what each approach does when the value is not a string, and how to choose an approach based on whether a string is required or merely optional.
Concept
An object variable can refer to an instance of almost any .NET type. Before treating that value as a more specific type, such as string, your code must make a decision:
- Require the existing object to already be a string: use a direct cast or pattern matching.
- Use the value only if it is a string: use
asor pattern matching. - Create text that represents the object: use
ToString()or, more safely in many cases,Convert.ToString().
These are different operations.
A cast does not convert an arbitrary object into a string. It checks whether the object already is compatible with string.
For example, an int value boxed into object is still an integer, not a string:
object value = 42;
Neither (string)value nor value as string turns it into "42". A conversion-to-text operation, such as value.ToString(), produces text instead.
Choosing correctly matters because the operations have different failure behavior. One throws an exception, one returns null, and one asks the object to provide a textual representation.
Mental Model
Think of an object reference as a box with an item inside.
- A direct cast says: “Open the box. This item must already be a string label.” If it is not, stop with an error.
- The
asoperator says: “Open the box. If it is a string label, give it to me; otherwise give me nothing (null).” ToString()says: “Whatever is in the box, ask it to write a text description of itself.” The result may be useful text, but it is not proof that the original item was a string.
The choice depends on whether the type is a strict requirement, an optional possibility, or whether you simply need displayable text.
Syntax and Examples
1. Direct cast: (string)o
object o = "Hello";
string s = (string)o;
Console.WriteLine(s); // Hello
A direct cast succeeds when the runtime object is a string or is compatible with string.
object o = 42;
string s = (string)o; // Throws InvalidCastException
Use this when receiving a non-string would be a programming error and you want the program to fail immediately at the incorrect assumption.
2. Safe reference cast: o as string
object o = "Hello";
string? s = o as string;
if (s != null)
{
Console.WriteLine(s); // Hello
}
If the runtime object is not a string, as returns null instead of throwing InvalidCastException.
object o = 42;
string? s = o as string;
Console.WriteLine(s is null); // True
Because failure is represented by null, the destination should normally be nullable (string?) when nullable reference types are enabled.
3. Text representation: o.ToString()
object o = 42;
string s = o.ToString();
Console.WriteLine(s); // 42
ToString() calls the object's method for creating a textual representation. It does not check whether the object is a string.
object o = new DateTime(2025, 1, 15);
string s = o.ToString();
The exact output can depend on the type and, for values such as dates and numbers, the current culture.
o.ToString()throwsNullReferenceExceptionifoisnull.
If o might be null and you want a safe string result, use:
string text = Convert.ToString(o) ?? string.Empty;
Convert.ToString(null) returns string.Empty.
Step by Step Execution
Consider this example:
object first = "Ada";
object second = 100;
string name = (string)first;
string? optionalName = second as string;
string numberText = second.ToString();
firstrefers to astringinstance containing"Ada".(string)firstchecks the runtime type. It is a string, so the cast succeeds andnamebecomes"Ada".secondrefers to a boxedintcontaining100.second as stringchecks whether that object is a string. It is not, so no exception is thrown;optionalNamebecomesnull.second.ToString()calls theToString()implementation forint. It produces the text"100", sonumberTextcontains"100".
Now compare a failing direct cast:
object second = 100;
string name = (string)second;
The runtime type is int, not string, so the assignment throws InvalidCastException. The following statements in that method are not executed unless the exception is handled.
Real World Use Cases
- Event handlers and UI controls: A handler may receive an
object sender. If a specific control type is required, validate or pattern-match it before using control-specific members. - JSON, database, or configuration data: Values can arrive as
object. Use conversion APIs when you need text, numbers, dates, or other representations rather than assuming the stored runtime type. - Optional metadata: A dictionary may contain values of different types. Use
as stringor pattern matching when a string value is optional. - Logging: Logging commonly needs text, so
ToString()or structured logging is appropriate. Logging does not usually require the original object to be a string. - Program invariants: If an internal API guarantees that a value is a string, a direct cast can expose violations immediately during testing and debugging.
Real Codebase Usage
Modern C# code often prefers pattern matching because it checks the type and gives you a correctly typed variable in one readable condition.
if (o is string text)
{
Console.WriteLine(text.Length);
}
This replaces a common older pattern:
string? text = o as string;
if (text != null)
{
Console.WriteLine(text.Length);
}
Use a guard clause when a required type is missing:
void HandleValue(object? value)
{
if (value is not string text)
{
throw new ArgumentException("A string value is required.", nameof(value));
}
Console.WriteLine(text.Trim());
}
For optional input, return early instead:
void PrintIfString(object? value)
{
if (value is not string text)
{
return;
}
Console.WriteLine(text);
}
For user-facing or serialized text, be explicit about formatting when needed:
using System.Globalization;
object amount = 1234.5m;
string text = Convert.ToString(amount, CultureInfo.InvariantCulture) ?? string.Empty;
This avoids relying accidentally on the machine's current culture for values that must have a stable format.
Common Mistakes
Expecting a cast to convert values
object value = 42;
string text = (string)value; // InvalidCastException
A cast checks type compatibility; it does not format 42 as "42".
string text = value.ToString();
Forgetting that as can return null
object value = 42;
string? text = value as string;
Console.WriteLine(text.Length); // NullReferenceException
Check for null, or use pattern matching:
if (value is string text)
{
Console.WriteLine(text.Length);
}
Calling ToString() when the object may be null
object? value = null;
string text = value.ToString(); // NullReferenceException
Use Convert.ToString or an explicit fallback:
string text = Convert.ToString(value) ?? string.Empty;
Using ToString() as validation
object value = 42;
string text = value.ToString();
This creates text but does not establish that the input was valid for a field that requires an actual string. For example, a username field may need a string input, not an arbitrary object that can be displayed as text.
Declaring a non-nullable result for as
With nullable reference types enabled, this is misleading:
string text = value as string;
The cast can fail and return null. Prefer:
string? text = value as string;
Comparisons
| Approach | Main purpose | If o is not a string | If o is null | Best use case |
|---|---|---|---|---|
(string)o | Require a compatible string object | Throws InvalidCastException | Produces null at runtime, though nullable warnings may apply | A string is guaranteed by an invariant or contract |
o as string | Try to obtain a string object | Returns null | Returns null | A string is optional and null is an acceptable “not a string” result |
o.ToString() | Produce textual representation | Usually returns type-specific text | Throws NullReferenceException | Displaying, logging, or formatting an existing non-null object |
Convert.ToString(o) | Produce text while handling null | Usually returns type-specific text | Returns string.Empty | General conversion where a null-safe text result is useful |
o is string text | Test and safely use a string | Condition is false | Condition is false | Preferred modern syntax for conditional type checks |
as works with reference types and nullable value types. It cannot be used to cast directly to a non-nullable value type:
object value = 10;
// int number = value as int; // Does not compile
int? number = value as int?; // Valid
For ordinary value-type checks, pattern matching is usually clearer:
if (value is int number)
{
Console.WriteLine(number + 1);
}
Cheat Sheet
// Required: throw if the value is not a string
string text = (string)value;
// Optional: null if the value is not a string
string? text = value as string;
// Modern optional check and typed variable
if (value is string text)
{
Console.WriteLine(text);
}
// Required type with clear validation
if (value is not string text)
{
throw new ArgumentException("Expected a string.");
}
// Text representation; value must not be null
string text = value.ToString();
// Null-safe conversion to text
string text = Convert.ToString(value) ?? string.Empty;
- Casts test whether the object is already compatible with the target type.
- Casts do not generally transform values between unrelated types.
asreturnsnullinstead of throwing for an incompatible reference type.ToString()creates a representation; the output may depend on the object's type and culture.- Prefer
is string textwhen you need to test and use a string in the same branch.
FAQ
Does (string)o convert an integer to text?
No. It succeeds only when the runtime object is compatible with string. Casting an int stored in object to string throws InvalidCastException.
Should I use as string or is string?
Prefer is string text when you want to test the type and use the value immediately. Use as string when assigning an optional result that will be used later.
What happens if o is null in (string)o?
The result is null at runtime because string is a reference type. With nullable reference types enabled, assign it to string? unless you have separately proven it is non-null.
What happens if o is null in o as string?
The result is null. No exception is thrown.
What happens if o is null in o.ToString()?
It throws NullReferenceException because there is no object instance on which to call the method.
Does ToString() always return useful text?
It returns whatever the type's ToString() implementation provides. Some custom types may return a default type name unless they override ToString().
Why can as not be used with int?
as returns null on failure, but a normal int cannot hold null. You can use int?, or more commonly use pattern matching: if (value is int number).
Which option is best for the original handler?
If the handler truly requires o to be a string, validate it with is not string text and throw a meaningful exception. If a non-string is normal and should be ignored, use if (o is string text). Use ToString() only when any object's text representation is acceptable.
Mini Project
Description
Build a small value formatter for data that arrives as object?, such as values from a configuration store, a grid, or a loosely typed API response. The formatter distinguishes actual strings from other values and safely handles null.
Goal
Create a method that reports whether a value is a string and produces a safe display value for every input.
Requirements
Requirement 1
Keep learning
Related questions
AddTransient vs AddScoped vs AddSingleton in ASP.NET Core Dependency Injection
Learn the differences between AddTransient, AddScoped, and AddSingleton in ASP.NET Core DI with examples and practical usage.
Best Way to Repeat a Character in C#: Building Repeated Strings Efficiently
Learn the best way to repeat a character in C#, compare StringBuilder, string concatenation, and simpler built-in options.
C# Access Modifiers and static: public, private, protected, and Defaults
Learn how C# public, private, protected, and default access control visibility, and how static differs from instance members.