Skip to content

Implement new concept exercise for extension methods #1585

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 36 commits into from
Sep 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
f0f6eb5
concept/extension-methods docs
Sep 5, 2021
b488af6
calculator-extension code files
Sep 5, 2021
f704f69
calculator-extension docs
Sep 5, 2021
303d596
update list of implemented exercises
Sep 5, 2021
c6bf7eb
Update exercises/concept/calculator-extensions/CalculatorExtensions.c…
yzAlvin Sep 9, 2021
89ec8b9
Update concepts/extension-methods/introduction.md
yzAlvin Sep 9, 2021
2a12b5b
Update concepts/extension-methods/about.md
yzAlvin Sep 14, 2021
1479c38
improve documentation
Sep 14, 2021
542a0e0
log extension method exercise
Sep 14, 2021
48b3385
added log exercise to config.json
Sep 14, 2021
84ac2e3
instructions for exercise
Sep 14, 2021
0ec63b3
merge conflict
Sep 14, 2021
2a5dd8e
Update exercises/concept/log-analysis/.docs/instructions.md
yzAlvin Sep 16, 2021
add9d0c
Update exercises/concept/log-analysis/.docs/instructions.md
yzAlvin Sep 16, 2021
b7c8fff
Update exercises/concept/log-analysis/.docs/instructions.md
yzAlvin Sep 16, 2021
ddaae8b
Update exercises/concept/log-analysis/LogAnalysis.csproj
yzAlvin Sep 16, 2021
9f83ca5
Update exercises/concept/log-analysis/.meta/config.json
yzAlvin Sep 16, 2021
7f2a9fd
Update exercises/concept/log-analysis/.docs/introduction.md
yzAlvin Sep 16, 2021
22645de
Update exercises/concept/calculator-conundrum/CalculatorConundrum.cs
yzAlvin Sep 16, 2021
2b0cc06
Update exercises/concept/log-analysis/.docs/hints.md
yzAlvin Sep 22, 2021
24da38d
update docs
Sep 22, 2021
3770c25
update code files
Sep 22, 2021
5d458f1
Update exercises/concept/log-analysis/.docs/hints.md
yzAlvin Sep 23, 2021
22437c2
Update exercises/concept/log-analysis/.docs/hints.md
yzAlvin Sep 23, 2021
f0dd81e
Update exercises/concept/log-analysis/.docs/instructions.md
yzAlvin Sep 23, 2021
8520235
Update exercises/concept/log-analysis/LogAnalysis.cs
yzAlvin Sep 23, 2021
ec8c825
Update exercises/concept/log-analysis/.docs/instructions.md
yzAlvin Sep 23, 2021
5227af3
Update config.json
yzAlvin Sep 23, 2021
8e6c7fd
Update config.json
yzAlvin Sep 23, 2021
9771aeb
Update exercises/concept/log-analysis/.docs/instructions.md
yzAlvin Sep 23, 2021
30efa9d
Update exercises/concept/log-analysis/.docs/instructions.md
yzAlvin Sep 23, 2021
92155c9
Update instructions.md
yzAlvin Sep 23, 2021
240d946
Update Exemplar.cs
yzAlvin Sep 23, 2021
c788161
add mention of namespace import
Sep 25, 2021
152e084
add test case
Sep 25, 2021
18673bb
Update exercises/concept/log-analysis/.docs/instructions.md
yzAlvin Sep 29, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions concepts/extension-methods/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"blurb": "Extension methods allow you to add methods to existing types.",
"authors": [
"yzAlvin"
]
}
22 changes: 22 additions & 0 deletions concepts/extension-methods/about.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# About

[Extension methods][extension-methods] allow adding methods to existing types without creating a new derived type, recompiling, or otherwise modifying the original type.

Extension methods are brought into scope at the namespace level.

Extension methods are static methods, but they're called as if they were instance methods on the extended type. It achieves this by using `this` before the type, indicating the instance we put the `.` on is passed as the first parameter. For client code, there's no apparent difference between calling an extension method and the methods defined in a type.

```csharp
public static int WordCount(this string str)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be nice to be a bit more explicit here and give the class a namespace, and then have a second code sample indicating another file that has a using statement for importing the extension method. That might make it a bit more explicit.

{
return str.Split().Length;
}

"Hello World".WordCount();
// => 2
```

A well-known example of extension methods are the [LINQ][linq] standard query operators that add query functionality to the existing IEnumerable types.

[linq]: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/linq/
[extension-methods]: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/extension-methods
13 changes: 13 additions & 0 deletions concepts/extension-methods/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Introduction

Extension methods allow adding methods to existing types without creating a new derived type, recompiling, or otherwise modifying the original type. They are defined as static methods but are called by using instance method syntax. Their first parameter is preceded by the `this` modifier, and specifies which type the method operates on, and are brought into scope at the namespace level.

```csharp
public static int WordCount(this string str)
{
return str.Split().Length;
}

"Hello World".WordCount();
// => 2
```
6 changes: 6 additions & 0 deletions concepts/extension-methods/links.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[
{
"url": "https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/extension-methods",
"description": "extension-methods"
}
]
14 changes: 14 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,15 @@
"overflow",
"integral-numbers"
]
},
{
"slug": "log-analysis",
"name": "Log Analysis",
"uuid": "4a940e54-eda8-4500-bcb2-25d83af01fb1",
"concepts": ["extension-methods"],
"prerequisites": [
"strings"
]
}
],
"practice": [
Expand Down Expand Up @@ -1931,6 +1940,11 @@
"slug": "expression-bodied-members",
"name": "Expression Bodied Members"
},
{
"uuid": "c1e57d81-8965-4e3f-96d1-44ff43b768e4",
"slug": "extension-methods",
"name": "Extension Methods"
},
{
"uuid": "0d0ce715-a7f2-42f4-b97b-54ba99b4df73",
"slug": "flag-enums",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ public static int Division(int operand1, int operand2)
{
return operand1 / operand2;
}

public static int Multiplication(int operand1, int operand2)
{
return operand1 * operand2;
Expand Down
24 changes: 24 additions & 0 deletions exercises/concept/log-analysis/.docs/hints.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Hints

## General

- All extension methods must be marked `static`.
- All extension methods must have `this` before the first argument

## 1. Implement the extension method SubstringAfter

- Different options to search for text in a string are explored in [this tutorial][tutorial-docs.microsoft.com-search-text-in-string].

## 2. Implement the extension method SubstringBetween

- Different options to search for text in a string are explored in [this tutorial][tutorial-docs.microsoft.com-search-text-in-string].

## 3. Parse message in a log

- You can re-use the `SubstringAfter()` extension method here.

## 4. Parse log level in a log

- You can re-use the `SubstringBetween()` extension method here.

[tutorial-csharp.net-strings]: https://csharp.net-tutorials.com/data-types/strings/
53 changes: 53 additions & 0 deletions exercises/concept/log-analysis/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Instructions

In this exercise you'll be processing log-lines.

Each log line is a string formatted as follows: `"[<LEVEL>]: <MESSAGE>"`.

There are three different log levels:

- `INFO`
- `WARNING`
- `ERROR`

You have several tasks, each of which will take a log line and ask you to do something with it.

## 1. Allow retrieving the string after a specific substring

Looking at the logs of the last month, you see that the test message is always located after a specific substring. As you're anticipating having to extract the test message sometime in the near future, you decide to create a helper method to help you with that.

Implement the (_static_) `LogAnalysis.SubstringAfter()` extension method, that takes in some string delimeter and returns the substring after the delimiter.

```csharp
var log = "[INFO]: File Deleted.";
log.SubstringAfter(": "); // => returns "File Deleted."
```

## 2. Allow retrieving the string in between two substrings

On further inspection, you see that the log level is always located between square brackets (`[` and `]`). As you're also anticipating having to extract the log level sometime in the near future, you decide to create a another helper method to help you with that.

Implement the (_static_) `LogAnalysis.SubstringBetween()` extension method that takes in two string delimeters, and returns the substring that lies between the two delimeters.

```csharp
var log = "[INFO]: File Deleted.";
log.SubstringBetween("[", "]"); // => returns "INFO"
```

## 3. Parse message in a log

Implement the (_static_) `LogAnalysis.Message()` extension method to return the message contained in a log.

```csharp
var log = "[ERROR]: Missing ; on line 20.";
log.Message(); // => returns "Missing ; on line 20."
```

## 4. Parse log level in a log

Implement the (_static_) `LogAnalysis.LogLevel()` extension method to return the log level of a log.

```csharp
var log = "[ERROR]: Missing ; on line 20.";
log.LogLevel(); // => returns "ERROR"
```
11 changes: 11 additions & 0 deletions exercises/concept/log-analysis/.docs/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Introduction

Extension methods allow adding methods to existing types without creating a new derived type, recompiling, or otherwise modifying the original type. They are defined as static methods but are called by using instance method syntax. Their first parameter is preceded by the 'this' modifier, and specifies which type the method operates on.

```csharp
public static int WordCount(this string str)
{
return str.Split().Length;
}
"Hello World".WordCount();
// => 2
24 changes: 24 additions & 0 deletions exercises/concept/log-analysis/.meta/Exemplar.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using System;

public static class LogAnalysis
{
public static string SubstringAfter(this string str, string delimiter)
{
return str.Substring(str.IndexOf(delimiter) + delimiter.Length);
}

public static string SubstringBetween(this string str, string start, string stop)
{
return str.SubstringAfter(start).Substring(0, str.IndexOf(stop) - 1);
}

public static string Message(this string log)
{
return log.SubstringAfter("]: ");
}

public static string LogLevel(this string log)
{
return log.SubstringBetween("[", "]");
}
}
18 changes: 18 additions & 0 deletions exercises/concept/log-analysis/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"blurb": "Learn about extension-methods by analysing logs.",
"icon": "log-levels",
"authors": [
"yzAlvin"
],
"files": {
"solution": [
"LogAnalysis.cs"
],
"test": [
"LogAnalysisTests.cs"
],
"exemplar": [
".meta/Exemplar.cs"
]
}
}
42 changes: 42 additions & 0 deletions exercises/concept/log-analysis/.meta/design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Design

## Goal

The goal of this exercise is to teach the student the Concept of Extension Methods in C#.

## Learning objectives

- Know what extension methods are.
- Know how to define extension methods on reference and value types

## Out of scope

- Assemblies.

## Concepts

- `extension-methods`: know what extension methods are; know how to define extension methods on reference and value types.

## Prerequisites

- `classes`: know what instance methods are.
- `namespaces`: know what namespaces are; know how to import namespaces.
- `collections`: know what collections are.

## Resources to refer to

### Hints

- [Extension methods][extension-methods]: how to define extension methods.
- [Namespaces][namespaces]: how to define an import namespaces.
- [Collections][collections]: what a collections is.

### After

- [Extension methods][extension-methods]: how to define extension methods.
- [Namespaces][namespaces]: how to define an import namespaces.
- [Collections][collections]: what a collections is.

[extension-methods]: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/extension-methods
[namespaces]: https://docs.microsoft.com/en-us/dotnet/csharp/fundamentals/types/namespaces
[collections]: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/collections
12 changes: 12 additions & 0 deletions exercises/concept/log-analysis/LogAnalysis.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System;

public static class LogAnalysis
{
// TODO: define the 'SubstringAfter()' extension method on the `string` type

// TODO: define the 'SubstringBetween()' extension method on the `string` type

// TODO: define the 'Message()' extension method on the `string` type

// TODO: define the 'LogLevel()' extension method on the `string` type
}
14 changes: 14 additions & 0 deletions exercises/concept/log-analysis/LogAnalysis.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
<PackageReference Include="Exercism.Tests" Version="0.1.0-beta1" />
</ItemGroup>

</Project>
38 changes: 38 additions & 0 deletions exercises/concept/log-analysis/LogAnalysisTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using Xunit;
using Exercism.Tests;
using System;

public class LogAnalysisTests
{
[Fact]
public void SubstringAfter_WithDelimeterOfLength1()
{
Assert.Equal(" am the 1st test", "I am the 1st test".SubstringAfter("I"));
}

[Fact(Skip = "Remove this Skip property to run this test")]
public void SubstringAfter_WithDelimeterOfLengthLongerThan1()
{
Assert.Equal(" test", "I am the 2nd test".SubstringAfter("2nd"));
}

[Fact(Skip = "Remove this Skip property to run this test")]
public void SubstringBetween()
{
Assert.Equal("INFO", "[INFO]: File Deleted.".SubstringBetween("[", "]"));
}

[Fact(Skip = "Remove this Skip property to run this test")]
public void Message()
{
var log = "[WARNING]: Library is deprecated.";
Assert.Equal("Library is deprecated.", log.Message());
}

[Fact(Skip = "Remove this Skip property to run this test")]
public void LogLevel()
{
var log = "[WARNING]: Library is deprecated.";
Assert.Equal("WARNING", log.LogLevel());;
}
}