---
title: "Validate an email address in C#"
url: https://recursiveai.org/onecontact/cookbook/validate-email-address/
description: "Validate an email address in C# with OneContact: free, open-source .NET code, a runnable example and real output."
version: "1.0.0"
---

# Validate an email address in C#

**OneContact** is a free, open-source contact data library for .NET from Recursive AI. MIT or Apache-2.0, no native dependencies, no network access.

[NuGet](https://www.nuget.org/packages/RecursiveAI.OneContact) · [GitHub](https://github.com/recursive-ai-oss/onecontact-csharp) · [Docs](https://recursiveai.org/onecontact/docs/)

Check email syntax and normalize the domain without sending a message.

```shell
dotnet add package RecursiveAI.OneContact
```

Requires [.NET 10](https://dotnet.microsoft.com/download/dotnet/10.0) (net10.0); .NET 9 and earlier aren’t supported.

```csharp
using RecursiveAI.OneContact;

bool valid = EmailAddress.TryParse(
    "jane@example.com", out var email);
Console.WriteLine(valid);
```

```text
True
```

**[Download this example (.zip)](https://recursiveai.org/onecontact/cookbook/validate-email-address/validate-email-address.zip)**: unzip, then `dotnet run`.

## Full program

```csharp
using RecursiveAI.OneContact;

foreach (string input in new[] { "Jane <jane@EXAMPLE.COM>", "not-an-email", "a..b@example.com" })
{
    if (EmailAddress.TryParse(input, out var email))
        Console.WriteLine($"{input} => {email.Canonical}");
    else
        Console.WriteLine($"{input} => invalid");
}
```

`dotnet run` prints:

```text
Jane <jane@EXAMPLE.COM> => jane@example.com
not-an-email => invalid
a..b@example.com => invalid
```

Rejected mailbox text makes `TryParse` return false; `Parse` instead throws `ContactParseException`. This is syntax validation, not a delivery check.

## Variations

Read an internationalized domain as ASCII and Unicode:

```csharp
using RecursiveAI.OneContact;

var email = EmailAddress.Parse("info@Bücher.example");
Console.WriteLine(email.Canonical);
Console.WriteLine(email.UnicodeDomain);
```

```text
info@xn--bcher-kva.example
bücher.example
```

## Related recipes

- [Validate a phone number in C#](https://recursiveai.org/onecontact/cookbook/validate-phone-number/)
- [Format a phone number as E.164 in C#](https://recursiveai.org/onecontact/cookbook/format-phone-number-e164/)
- [Parse a phone number in C#](https://recursiveai.org/onecontact/cookbook/parse-phone-number/)
- [Extract email addresses from text in C#](https://recursiveai.org/onecontact/cookbook/extract-emails-from-text/)

## The short answer

Use `EmailAddress.TryParse` to check whether OneContact can read one mailbox. It returns a Boolean and, on success, a parsed value with the canonical address, display name and flags. This checks syntax and domain structure. It does not verify that a mailbox exists or that the person entering it controls it.

The example accepts `Jane <jane@EXAMPLE.COM>` and normalizes it to `jane@example.com`. That is intentional: this parser accepts both a bare address and a display name with an address. If your form requires a bare address only, make that an explicit form policy rather than assuming every successful parse had no display name.

## How can I validate email addresses in C#?

Pick the rule your form needs before deciding what “valid” means. OneContact reads one mailbox, including quoted local parts, comments and internationalized domains. It does not treat a list of recipients or a mail group as one mailbox. The [email documentation](https://recursiveai.org/onecontact/docs/email/) spells out the accepted syntax and the flags returned for it.

The canonical value keeps the local part as written, including meaningful quotes, and lowercases the domain. Internationalized domains become ASCII A-labels; `UnicodeDomain` exposes the readable form. The variation demonstrates both outputs. Don’t lowercase the whole canonical value merely because domains are case-insensitive: the documented canonicalization preserves the local part.

Read flags when your application needs stricter rules. For example, `HasDisplayName`, `QuotedLocalPart`, `SingleLabelDomain` and `ObsoleteSyntax` describe accepted text. Length issues are reported with `ExceedsLengthLimit` and warnings, not parse refusal. A successful Boolean parse therefore does not imply that every delivery-system length constraint passed. Define whether to reject or warn about those cases in your application.

Hints such as `PossibleTypo` and `TypoSuggestion` are not automatic corrections. Offer a suggestion to the user instead of silently replacing a domain. Likewise, role-account and disposable-domain flags describe classifications; they do not establish who owns an address.

## Do I need a regex to validate an email address in C#?

Not for the examples here. The parser supplies the syntax check and a normalized value in the same operation. A simple pattern can be a form-specific precheck, but it should not become an undocumented substitute for the accepted mailbox grammar.

[Microsoft’s email-format guidance](https://learn.microsoft.com/en-us/dotnet/standard/base-types/how-to-verify-that-strings-are-in-valid-email-format) makes the distinction explicit: checking structure is not a substitute for verifying that the address exists, and it recommends sending a test email. OneContact itself opens no network connection. Build any confirmation flow outside the parser.

Finally, validation and extraction are different jobs. A validator expects the whole input to be one mailbox. A paragraph containing several addresses is not one mailbox, even when every address inside it is well formed. Use the [text extraction recipe](https://recursiveai.org/onecontact/cookbook/extract-emails-from-text/) for that input, then apply your acceptance rules to each extracted value. Keep the original text alongside the canonical value when you need to explain a rejected import row or let a user edit what they entered.

As the [OneContact release overview](https://recursiveai.org/onecontact/docs/overview/) states: “Version 1.0 runs on rules and markup alone.”
