-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit acf0d55
Showing
175 changed files
with
12,505 additions
and
0 deletions.
There are no files selected for viewing
Empty file.
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"><head> | ||
<meta charset="utf-8"> | ||
<meta http-equiv="X-UA-Compatible" content="IE=edge"> | ||
<meta name="viewport" content="width=device-width, initial-scale=1"> | ||
<link rel="shortcut icon" sizes="16x16 32x32 48x48" href="/assets/favicon/favicon.ico"> | ||
<link rel="apple-touch-icon" href="/assets/favicon/apple-touch-icon.png"><!-- Begin Jekyll SEO tag v2.7.1 --> | ||
<title>How to call Program.Main method if you’re using top-level statements | alexeyfv</title> | ||
<meta name="generator" content="Jekyll v4.2.1" /> | ||
<meta property="og:title" content="How to call Program.Main method if you’re using top-level statements" /> | ||
<meta name="author" content="© 2024 Alexey Fedorov" /> | ||
<meta property="og:locale" content="en_US" /> | ||
<meta name="description" content="Lately, I’ve been writing a lot of services that use top-level statements. The code with this kind of syntax sugar simplifies the Main method and improves readability. However, on the other hand, it makes it more difficult to create integration tests for the services. There isn’t simply way to call the Main method and pass the necessary arguments." /> | ||
<meta property="og:description" content="Lately, I’ve been writing a lot of services that use top-level statements. The code with this kind of syntax sugar simplifies the Main method and improves readability. However, on the other hand, it makes it more difficult to create integration tests for the services. There isn’t simply way to call the Main method and pass the necessary arguments." /> | ||
<link rel="canonical" href="http://www.alexeyfv.xyz//2023/08/10/top-level-statements.html" /> | ||
<meta property="og:url" content="http://www.alexeyfv.xyz//2023/08/10/top-level-statements.html" /> | ||
<meta property="og:site_name" content="alexeyfv" /> | ||
<meta property="og:type" content="article" /> | ||
<meta property="article:published_time" content="2023-08-10T00:00:00+00:00" /> | ||
<meta name="twitter:card" content="summary" /> | ||
<meta property="twitter:title" content="How to call Program.Main method if you’re using top-level statements" /> | ||
<script type="application/ld+json"> | ||
{"author":{"@type":"Person","name":"© 2024 Alexey Fedorov"},"description":"Lately, I’ve been writing a lot of services that use top-level statements. The code with this kind of syntax sugar simplifies the Main method and improves readability. However, on the other hand, it makes it more difficult to create integration tests for the services. There isn’t simply way to call the Main method and pass the necessary arguments.","@type":"BlogPosting","headline":"How to call Program.Main method if you’re using top-level statements","dateModified":"2023-08-10T00:00:00+00:00","datePublished":"2023-08-10T00:00:00+00:00","mainEntityOfPage":{"@type":"WebPage","@id":"http://www.alexeyfv.xyz//2023/08/10/top-level-statements.html"},"url":"http://www.alexeyfv.xyz//2023/08/10/top-level-statements.html","@context":"https://schema.org"}</script> | ||
<!-- End Jekyll SEO tag --> | ||
<link rel="stylesheet" href="/assets/css/style.css"><link type="application/atom+xml" rel="alternate" href="http://www.alexeyfv.xyz//feed.xml" title="alexeyfv" /> | ||
</head><body><header class="site-header"> | ||
|
||
<div class="wrapper"><a class="site-title" rel="author" href="/">alexeyfv</a><nav class="site-nav"> | ||
<input type="checkbox" id="nav-trigger" class="nav-trigger" /> | ||
<label for="nav-trigger"> | ||
<span class="menu-icon"> | ||
<svg viewBox="0 0 18 15" width="18px" height="15px"> | ||
<path d="M18,1.484c0,0.82-0.665,1.484-1.484,1.484H1.484C0.665,2.969,0,2.304,0,1.484l0,0C0,0.665,0.665,0,1.484,0 h15.032C17.335,0,18,0.665,18,1.484L18,1.484z M18,7.516C18,8.335,17.335,9,16.516,9H1.484C0.665,9,0,8.335,0,7.516l0,0 c0-0.82,0.665-1.484,1.484-1.484h15.032C17.335,6.031,18,6.696,18,7.516L18,7.516z M18,13.516C18,14.335,17.335,15,16.516,15H1.484 C0.665,15,0,14.335,0,13.516l0,0c0-0.82,0.665-1.483,1.484-1.483h15.032C17.335,12.031,18,12.695,18,13.516L18,13.516z"/> | ||
</svg> | ||
</span> | ||
</label> | ||
|
||
<div class="trigger"><a class="page-link" href="/tags/">Tags</a><a class="page-link" href="/cv/">CV</a><a class="page-link" href="/about/">About</a></div> | ||
</nav></div> | ||
</header> | ||
<main class="page-content" aria-label="Content"> | ||
<div class="wrapper"> | ||
<article | ||
class="post h-entry" | ||
itemscope | ||
itemtype="http://schema.org/BlogPosting" | ||
> | ||
<link rel="stylesheet" href="/static/glide/css/glide.core.min.css" /> | ||
<link rel="stylesheet" href="/static/glide/css/glide.theme.min.css" /> | ||
<script type="text/javascript" src="/static/glide/glide.min.js"></script> | ||
<link href="/static/syntax-highlighting.css" rel="stylesheet" /> | ||
<link href="/static/image.css" rel="stylesheet" /> | ||
<script> | ||
function initGlideCarousel() { | ||
var sliders = document.querySelectorAll(".glide"); | ||
const conf = {}; | ||
sliders.forEach((item) => { | ||
new Glide(item, conf).mount(); | ||
}); | ||
} | ||
document.addEventListener("DOMContentLoaded", initGlideCarousel); | ||
</script> | ||
<header class="post-header"> | ||
<h1 class="post-title p-name" itemprop="name headline"> | ||
How to call Program.Main method if you're using top-level statements | ||
</h1> | ||
<p class="post-meta"><time | ||
class="dt-published" | ||
datetime="2023-08-10T00:00:00+00:00" | ||
itemprop="datePublished" | ||
> | ||
Aug 10, 2023 | ||
</time></p> | ||
|
||
<a class="post" href="/tag/csharp">#csharp</a> | ||
|
||
</header> | ||
|
||
<div class="post-content e-content" itemprop="articleBody"><p>Lately, I’ve been writing a lot of services that use <a href="https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/tutorials/top-level-statements">top-level statements</a>. The code with this kind of syntax sugar simplifies the <code class="language-plaintext highlighter-rouge">Main</code> method and improves readability. However, on the other hand, it makes it more difficult to create integration tests for the services. There isn’t simply way to call the <code class="language-plaintext highlighter-rouge">Main</code> method and pass the necessary arguments.</p> | ||
|
||
<p>Since Microsoft introduced top-level statements in C# 9, you no longer need to write a weird Java-like <code class="language-plaintext highlighter-rouge">public static void Main(string[] args)</code> method. So the code</p> | ||
|
||
<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">System</span><span class="p">;</span> | ||
|
||
<span class="k">namespace</span> <span class="nn">Application</span> | ||
<span class="p">{</span> | ||
<span class="k">public</span> <span class="k">class</span> <span class="nc">Program</span> | ||
<span class="p">{</span> | ||
<span class="k">public</span> <span class="k">static</span> <span class="k">void</span> <span class="nf">Main</span><span class="p">(</span><span class="kt">string</span><span class="p">[]</span> <span class="n">args</span><span class="p">)</span> | ||
<span class="p">{</span> | ||
<span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">$"args: </span><span class="p">{</span><span class="kt">string</span><span class="p">.</span><span class="nf">Join</span><span class="p">(</span><span class="s">","</span><span class="p">,</span> <span class="n">args</span><span class="p">)}</span><span class="s">"</span><span class="p">);</span> | ||
<span class="p">}</span> | ||
<span class="p">}</span> | ||
<span class="p">}</span> | ||
</code></pre></div></div> | ||
|
||
<p>can be written just in one line:</p> | ||
|
||
<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">System</span><span class="p">.</span><span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">$"args: </span><span class="p">{</span><span class="kt">string</span><span class="p">.</span><span class="nf">Join</span><span class="p">(</span><span class="s">","</span><span class="p">,</span> <span class="n">args</span><span class="p">)}</span><span class="s">"</span><span class="p">);</span> | ||
</code></pre></div></div> | ||
|
||
<p>But what if we want to call this code and pass some arguments? By default, the C# compiler will convert the code above to the <a href="https://sharplab.io/#v2:D4AQTAjAsAULIQJwAoAkAiAhgJwOYGcAuAAgG8EAGAOgCkB7ASwDtl0Aad4nAgSgF90PANywgA==">following code</a>:</p> | ||
|
||
<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">internal</span> <span class="k">class</span> <span class="nc">Program</span> | ||
<span class="p">{</span> | ||
<span class="k">private</span> <span class="k">static</span> <span class="k">void</span> <span class="p"><</span><span class="n">Main</span><span class="p">></span><span class="err">$</span><span class="p">(</span><span class="kt">string</span><span class="p">[]</span> <span class="n">args</span><span class="p">)</span> | ||
<span class="p">{</span> | ||
<span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="kt">string</span><span class="p">.</span><span class="nf">Concat</span><span class="p">(</span><span class="s">"args: "</span><span class="p">,</span> <span class="kt">string</span><span class="p">.</span><span class="nf">Join</span><span class="p">(</span><span class="s">","</span><span class="p">,</span> <span class="n">args</span><span class="p">)));</span> | ||
<span class="p">}</span> | ||
<span class="p">}</span> | ||
</code></pre></div></div> | ||
|
||
<p>The class is internal, and the method is private. You cannot call the <code class="language-plaintext highlighter-rouge"><Main></code> method directly, especially from another assembly. How can you overcome this?</p> | ||
|
||
<p>First of all, you should declare the <code class="language-plaintext highlighter-rouge">Program</code> class as <code class="language-plaintext highlighter-rouge">public partial</code>. This makes the <code class="language-plaintext highlighter-rouge">Program</code> class visible in external code. If you can’t change the code, you can load the type from the assembly using <a href="https://learn.microsoft.com/en-us/dotnet/api/system.reflection.assembly.gettype">Assembly.GetType</a>.</p> | ||
|
||
<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">System</span><span class="p">.</span><span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">$"args: </span><span class="p">{</span><span class="kt">string</span><span class="p">.</span><span class="nf">Join</span><span class="p">(</span><span class="s">","</span><span class="p">,</span> <span class="n">args</span><span class="p">)}</span><span class="s">"</span><span class="p">);</span> | ||
|
||
<span class="k">public</span> <span class="k">partial</span> <span class="k">class</span> <span class="nc">Program</span> <span class="p">{</span> <span class="p">}</span> | ||
</code></pre></div></div> | ||
|
||
<p>Now that the <code class="language-plaintext highlighter-rouge">Program</code> class has <a href="https://sharplab.io/#v2:D4AQTAjAsAULIQJwAoAkAiAhgJwOYGcAuAAgG8EAGAOgCkB7ASwDtl0Aad4nAgSgF90PANyx4AZmIAHHABcGmADbFwyiAHYyxPrCA===">become visible</a> from other assemblies, the <code class="language-plaintext highlighter-rouge"><Main></code> method is still private. Using reflection, we can retrieve the <code class="language-plaintext highlighter-rouge"><Main></code> method from the <code class="language-plaintext highlighter-rouge">DeclaredMethods</code> collection and call it using <code class="language-plaintext highlighter-rouge">Invoke</code>:</p> | ||
|
||
<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">var</span> <span class="n">typeInfo</span> <span class="p">=</span> <span class="k">typeof</span><span class="p">(</span><span class="n">Program</span><span class="p">)</span> <span class="k">as</span> <span class="n">TypeInfo</span> <span class="p">??</span> <span class="k">throw</span> <span class="k">new</span> <span class="nf">InvalidOperationException</span><span class="p">();</span> | ||
<span class="kt">var</span> <span class="n">main</span> <span class="p">=</span> <span class="n">typeInfo</span><span class="p">.</span><span class="n">DeclaredMethods</span><span class="p">.</span><span class="nf">First</span><span class="p">(</span><span class="n">m</span> <span class="p">=></span> <span class="n">m</span><span class="p">.</span><span class="n">Name</span> <span class="p">==</span> <span class="s">"<Main>"</span><span class="p">);</span> | ||
<span class="kt">var</span> <span class="n">args</span> <span class="p">=</span> <span class="k">new</span><span class="p">[]</span> <span class="p">{</span> <span class="s">"arg1"</span><span class="p">,</span> <span class="s">"arg2"</span> <span class="p">};</span> | ||
<span class="n">main</span><span class="p">.</span><span class="nf">Invoke</span><span class="p">(</span><span class="k">null</span><span class="p">,</span> <span class="k">new</span> <span class="kt">object</span><span class="p">[]</span> <span class="p">{</span> <span class="n">args</span> <span class="p">});</span> | ||
</code></pre></div></div> | ||
|
||
<p>That’s it. Reflection provides a powerful mechanism which helps us with writing tests for programs that use top-level statements.</p> | ||
</div><a class="u-url" href="/2023/08/10/top-level-statements.html" hidden></a> | ||
</article> | ||
|
||
<script | ||
src="https://utteranc.es/client.js" | ||
repo="alexeyfv/blog" | ||
issue-term="url" | ||
label="comment" | ||
theme="github-dark" | ||
crossorigin="anonymous" | ||
async | ||
></script> | ||
|
||
</div> | ||
</main><footer class="site-footer h-card"> | ||
<data class="u-url" href="/"></data> | ||
|
||
<div class="wrapper"> | ||
|
||
<div class="footer-col-wrapper"> | ||
<div class="footer-col"> | ||
<ul class="contact-list"> | ||
<li class="p-name">© 2024 Alexey Fedorov</li> | ||
<li class="p-name">Powered by | ||
<a href="https://github.com/jekyll/jekyll">Jekyll</a> | ||
& | ||
<a href="https://github.com/jekyll/minima">Minima</a> | ||
</li> | ||
</ul> | ||
</div> | ||
<div class="footer-col"> | ||
<p>Articles about .NET, C# and much more</p> | ||
</div> | ||
</div> | ||
|
||
<div class="social-links"><ul class="social-media-list"><li><a rel="me" href="https://github.com/alexeyfv" target="_blank" title="alexeyfv"><svg class="svg-icon grey"><use xlink:href="/assets/minima-social-icons.svg#github"></use></svg></a></li><li><a rel="me" href="https://www.linkedin.com/in/alexeyfv" target="_blank" title="alexeyfv"><svg class="svg-icon grey"><use xlink:href="/assets/minima-social-icons.svg#linkedin"></use></svg></a></li><li><a rel="me" href="https://t.me/alexeyfv" target="_blank" title="alexeyfv"><svg class="svg-icon grey"><use xlink:href="/assets/minima-social-icons.svg#telegram"></use></svg></a></li></ul></div> | ||
|
||
</div> | ||
|
||
</footer></body> | ||
|
||
</html> |
Oops, something went wrong.