Modify the behaviour of WriteLine method? - c#

I need a very simple to imagine thing - modify System.Console class method WriteLine to write not only in console, but also in text log file.
I simply need to run this function with preset arguments prior to any call to WriteLine and then do generic WriteLine:
class Logger
{
static public void WriteLog(string logMessage, string fileName ,bool addTimeStamp = true)
{
//Getting temp folder
string destPath = Path.GetTempPath();
//Creating folder
if (!Directory.Exists(destPath))
Directory.CreateDirectory(destPath);
//Naming log file
var filePath = String.Format("{0}\\{1}.log",
destPath,
fileName
);
//Writing to timestamp
if (addTimeStamp)
{
logMessage = String.Format("[{0}] - {1}{2}",
DateTime.Now.ToString("HH:mm:ss", CultureInfo.CurrentCulture),
logMessage,
Environment.NewLine);
}
else
{
logMessage = String.Format("{0}{1}",
logMessage,
Environment.NewLine);
}
//Writing to log
File.AppendAllText(filePath, logMessage);
}
}
I thought about inheritance, but I cannot even create a class since "Static class ConsoleX cannot derive from type Console. Static classes must derive from object".
Is there at all simple way to wrap around WriteLine method? I would do a separate (not inherited) class for it but then I need to create like 18 overloads for this method just to pass through arguments to 18 overloads of generic WriteLine, so it feels like waste of effort for seemingly simple thing.

Console.WriteLine is equivalent to Console.Out.WriteLine, so you can implement your own TextWriter and pass it to Console.SetOut to achieve your purpose.
BTW a class can not inherit a static class in any way.
class ConsoleOut : TextWriter
{
private TextWriter _oldOut = Console.Out;
// This overload method is the base method of all
// Write or WriteLine overloads.
// But you can override other methods as you want
public override void Write(char[] buffer, int index, int count)
{
// Write to the original console
_oldOut.Write(buffer, index, count);
// Write to file
Logger.Write(buffer, index, count);
}
}
Console.SetOut(new ConsoleOut());
BTW 2, the default console ouput is synchronized, but your method isn't, this is what you need to consider, because you may get IOException.

It is not possible to create a derived class from a static class in C#. A static class is a class that cannot be instantiated and therefore cannot be inherited from. Instead, you can use a regular class or an abstract class as a base for a derived class.

Related

C# class automatically calls its method

Recently I was browsing one C# program and stumbled across some weird behavior of a class inside of it. I wrote a little program to outline this behavior:
class Program
{
static void Main()
{
Test inst1 = new Test();
inst1.name0 = "Hello";
inst1.name1 = "World";
Console.WriteLine(inst1.ToString());
Console.WriteLine(inst1);
}
}
class Test
{
public string name0;
public string name1;
public override string ToString()
{
return string.Format("Name0: {0}; Name1: {1}", name0 ?? "(null)", name1 ?? "(null)");
}
}
Output:
Name0: Hello; Name1: World
Name0: Hello; Name1: World
So why do inst1.ToString() and inst1 return the same value in WriteLine() method? I'm confused.
When passed an object, the Console.WriteLine overload will use the object's ToString method.
From the documentation,
If value is null, only the line terminator is written. Otherwise, the ToString method of value is called to produce its string representation, and the resulting string is written to the standard output stream.
This happens because the Test class overrides the ToString function.
When Console.Writeline has to display a non String object, it will look for the ToString function and use it if available.

Can we assign TextContext.TestDeploymentDir value to a static class data member?

I am very new to CodedUI test automation framework. I came across TestContext which has the important information regarding test result outputs and directories.
Actually i have created a static Logger class which writes output data to a .txt file. Now i want to create it under TestResults folder. Everytime i am running the test method it is creating a folder followed by some timestamp. I want to create my Results.txt file in that location.
Below is the code i am using :
public static class Logger
{
string logLocation = TestContext.TestDeploymentDir + "\\Results.txt";
static Logger() {
File.Create(logLocation);
using (var fs = new FileStream(logLocation, FileMode.Truncate))
{
}
}
public static void ResultLog(int testcasenum,String Expected,String Actual, String textResult)
{
FileInfo outtxt = new FileInfo(logLocation);
StreamWriter logline = outtxt.AppendText();
logline.WriteLine("Test Case : " + testcasenum);
logline.WriteLine("{0},{1},{2}", "Expected - "+Expected, "Actual - "+Actual, "Result - "+textResult);
// flush and close file.
logline.Flush(); logline.Close();
}
}
Now i am getting a compile time error saying A field initializer cannot reference the non-static field, method, or property TestContext.TestDeploymentDir. Not sure how to resolve this error or whether it is possible or not ?
You're going to need to mark logLocation as static since it is contained in a static class. It's kind of silly but all members of a static class do also need to be marked static. I believe this is to prevent confusion when reading larger classes where you cannot see the class declaration. Next your current error message also says TestContext.TestDeploymentDir is not marked static and you'll need to modify that as well if possible. If not you'll need to implement a singleton pattern to provide a static copy of an instance of that class. Depending on the way that class works it may or may not be possible.
Finally figured out a way to get the out path of Coded UI framework. Below is the code i have written :
public static class Logger
{
static string uripath = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase) + "\\Results.txt";
public static string logLocation = new Uri(uripath).LocalPath;
static Logger() {
using (File.Create(logLocation))
{ }
using (var fs = new FileStream(logLocation, FileMode.Truncate)){}
}
public static void ResultLog(int testcasenum,String Expected,String Actual, String textResult)
{
FileInfo outtxt = new FileInfo(logLocation);
StreamWriter logline = outtxt.AppendText();
logline.WriteLine("Test Case : " + testcasenum);
logline.WriteLine("{0},{1},{2}", "Expected - "+Expected, "Actual - "+Actual, "Result - "+textResult);
// flush and close file.
logline.Flush(); logline.Close();
}
}
uripath will contain the same path as that of TestContext.TestDeploymentDir. Now Results.txt will come as an attachment in Test Explorer and we can see the output.

Store reference to method to be called later with different parameters

I need to be able to have ONE variable type store references to different methods, then call them later with different parameters, and have them return something. The methods will each have different parameter types and output types. I've tried delegates, actions and Func.
Pseudo code example below
public void Open(String directory){
File.Open(Directory);
}
public string ChangeVolume(int volume){
//Code to change volume
//return the new volume
}
public static void Main{
MyVaribleType var1 = Open;
myVaribleType var2 = ChangeVolume;
var1("C:\Windows");
Console.WriteLine(var2(100) );
}
How would I accomplish this with a single variable type?
You can hold references to the methods in collection of MethodInfo class provided by reflection APIs. To be able to call these methods you will also need to keep the references of the objects on which you want to call these methods. You can use reflection itself to invoke these methods. Reflection is slow so you can use this method only if you don't have high performance requirements.
You can't do it quite as you want to: the two methods have different signatures as #Mephy noted. You can do it using delegates, though:
public delegate FileStream OpenFileHandler(string filePath);
public delegate string ChangeVolumeHandler(FileStream stream, int volume);
class Program
{
private static FileStream Open(string filePath)
{
return File.Open(filePath, FileMode.OpenOrCreate);
}
private static string ChangeVolume(FileStream stream, int volume)
{
return "Done! Honest!";
}
static void Main(string[] args)
{
OpenFileHandler ofh = Program.Open;
ChangeVolumeHandler cvh = Program.ChangeVolume;
FileStream stream = ofh("path");
string xyzzy = cvh(stream, 100);
}
}
I'm not sure why you'd want to, but I assume that this is a rather more complex requirement that you've shown, so fine.
Note that in addition to having to use two delegates, you aloso have to pass each the items it needs to work on and that it creates (e.g. the FileStream object).
You can use an ExpandoObject:
dynamic d = new ExpandoObject();
d.ChangeVolume = new Func<int, string>(ChangeVolume);
and then call it thusly:
var result = d.ChangeVolume(volume); //ChangeVolume is type-checked at runtime.
or simply write the logic into the new dynamic property directly:
d.ChangeVolume = new Func<int, string>(x => (x * x).ToString()); // or whatever

C# constructors with same parameter signatures

I'm sure this must be a common problem. I've got a class that in an ideal world would have the following constructors
public Thing(string connectionString)
public Thing(string fileName)
Obviously this isn't allowed because the signatures are the same. Does anybody know of an elegant solution to this problem?
You can used the named constructor idiom:
public class Thing
{
private string connectionString;
private string filename;
private Thing()
{
/* Make this private to clear things up */
}
public static Thing WithConnection(string connectionString)
{
var thing = new Thing();
thing.connectionString = connectionString;
return thing;
}
public static Thing WithFilename(string filename)
{
var thing = new Thing();
thing.filename = filename;
return thing;
}
}
Well, there are several potentials - what's considered elegent depends on the usage scenario.
Static factory methods, that call into a private constructor.
static Thing thingWithFileName(string fileName)
Create a different type for one of the parameters, or use a builtin. Rather than a string fileName, you could use a System.IO.FileStream. This is also more type safe, as I can't accidently pass the wrong data into the wrong static method, or field.
Pass a second parameter to the constructor, either an enum or a boolean, indicating the intent of the first parameter
enum ThingType { FileName, ConnectionString }
Thing(string str, ThingType type) ...
Subclass Thing, so you have a ConnectionTypeThing and a FileBackedThing
Completely eliminate Thing doing it's connection, and have preconnected data sources provided. So you end up with
Thing(InputStream dataSource)
or something analogous.
My "elegance" money goes on either the first or second suggestions, but I'd need more context to be happy with any choice.
You can make all the constructors private and create factory methods (static methods on the class like CreateFromConnectionString()).
These actually seem like different "things" to me, either a class associated with a file or a class associated with a database. I'd define an interface, then have separate implementations for each. Use a Factory to generate the correct implementation.
A hint that you may need to change your design is if your methods have to decide whether they are working with a file or a database before they perform the required action. If this is the case, then separating into different classes would be the way I would go.
public interface IThing
{
... methods to do the things that Things do
}
public class FileThing : IThing
{
... file-based methods
}
public class DatabaseThing : IThing
{
... database-based methods
}
public static class ThingFactory
{
public IThing GetFileThing( string name )
{
return new FileThing( name );
}
public IThing GetDatabaseThing( string connectionString )
{
return new DatabaseThing( connectionString );
}
}
If you had common behavior you could alternatively define an abstract class containing the default/common behavior and derive from it instead of/in addition to the interface.
Make two public properties ConnectionString and FileName and then use these to fill your object.
In C# you can use an object initalizer. Like this:
Thing thing = new Thing{FileName = "abc", ConnectionString = "123"};
Here are some workarounds.
Have one constructor that takes a connection string, and then have a factory method on the class that takes filename. Something like this:
public static Thing CreateThing(string fileName)
this method can call a private parameter less constructor, and you can take it from there.
Another option, is to have an enum that has two types in it. FileName and ConnectionString. Then just have one constructor that takes a string, and the enum. Then based on the enum you can determine which way to go.
I like static constructor-functions:
class Thing
{
public static Thing NewConnection(string connectionString)
{
return new Thing(connectionString, true);
}
public static Thing NewFile(string fileName);
{
return new Thing(fileName, false);
}
}
.
.
.
{
var myObj = Thing.NewConnection("connect=foo");
var Obj2 = Thing.NewFile("myFile.txt");
}
(not shown, but straight-forward, the implementation of the Thing-Constructor with an extra boolean parameter).

Can I add extension methods to an existing static class?

I'm a fan of extension methods in C#, but haven't had any success adding an extension method to a static class, such as Console.
For example, if I want to add an extension to Console, called 'WriteBlueLine', so that I can go:
Console.WriteBlueLine("This text is blue");
I tried this by adding a local, public static method, with Console as a 'this' parameter... but no dice!
public static class Helpers {
public static void WriteBlueLine(this Console c, string text)
{
Console.ForegroundColor = ConsoleColor.Blue;
Console.WriteLine(text);
Console.ResetColor();
}
}
This didn't add a 'WriteBlueLine' method to Console... am I doing it wrong? Or asking for the impossible?
No. Extension methods require an instance variable (value) for an object. You can however, write a static wrapper around the ConfigurationManager interface. If you implement the wrapper, you don't need an extension method since you can just add the method directly.
public static class ConfigurationManagerWrapper
{
public static ConfigurationSection GetSection( string name )
{
return ConfigurationManager.GetSection( name );
}
.....
public static ConfigurationSection GetWidgetSection()
{
return GetSection( "widgets" );
}
}
Can you add static extensions to classes in C#? No but you can do this:
public static class Extensions
{
public static T Create<T>(this T #this)
where T : class, new()
{
return Utility<T>.Create();
}
}
public static class Utility<T>
where T : class, new()
{
static Utility()
{
Create = Expression.Lambda<Func<T>>(Expression.New(typeof(T).GetConstructor(Type.EmptyTypes))).Compile();
}
public static Func<T> Create { get; private set; }
}
Here's how it works. While you can't technically write static extension methods, instead this code exploits a loophole in extension methods. That loophole being that you can call extension methods on null objects without getting the null exception (unless you access anything via #this).
So here's how you would use this:
var ds1 = (null as DataSet).Create(); // as oppose to DataSet.Create()
// or
DataSet ds2 = null;
ds2 = ds2.Create();
// using some of the techniques above you could have this:
(null as Console).WriteBlueLine(...); // as oppose to Console.WriteBlueLine(...)
Now WHY did I pick calling the default constructor as an example, and AND why don't I just return new T() in the first code snippet without doing all of that Expression garbage?
Well todays your lucky day because you get a 2fer. As any advanced .NET developer knows, new T() is slow because it generates a call to System.Activator which uses reflection to get the default constructor before calling it. Damn you Microsoft!
However my code calls the default constructor of the object directly.
Static extensions would be better than this but desperate times call for desperate measures.
It's not possible.
And yes, I think MS made a mistake here.
Their decision does not make sense and forces programmers to write (as described above) a pointless wrapper class.
Here is a good example: Trying to extend static MS Unit testing class Assert: I want 1 more Assert method AreEqual(x1,x2).
The only way to do this is to point to different classes or write a wrapper around 100s of different Assert methods. Why!?
If the decision was being made to allow extensions of instances, I see no logical reason to not allow static extensions. The arguments about sectioning libraries does not stand up once instances can be extended.
I stumbled upon this thread while trying to find an answer to the same question the OP had. I didn't find the answer I wanted, but I ended up doing this.
public static class Helpers
{
public static void WriteLine(this ConsoleColor color, string text)
{
Console.ForegroundColor = color;
Console.WriteLine(text);
Console.ResetColor();
}
}
And I use it like this:
ConsoleColor.Cyan.WriteLine("voilĂ ");
As of C#7 this isn't supported. There are however discussions about integrating something like that in C#8 and proposals worth supporting.
Maybe you could add a static class with your custom namespace and the same class name:
using CLRConsole = System.Console;
namespace ExtensionMethodsDemo
{
public static class Console
{
public static void WriteLine(string value)
{
CLRConsole.WriteLine(value);
}
public static void WriteBlueLine(string value)
{
System.ConsoleColor currentColor = CLRConsole.ForegroundColor;
CLRConsole.ForegroundColor = System.ConsoleColor.Blue;
CLRConsole.WriteLine(value);
CLRConsole.ForegroundColor = currentColor;
}
public static System.ConsoleKeyInfo ReadKey(bool intercept)
{
return CLRConsole.ReadKey(intercept);
}
}
class Program
{
static void Main(string[] args)
{
try
{
Console.WriteBlueLine("This text is blue");
}
catch (System.Exception ex)
{
Console.WriteLine(ex.Message);
Console.WriteLine(ex.StackTrace);
}
Console.WriteLine("Press any key to continue...");
Console.ReadKey(true);
}
}
}
Nope. Extension method definitions require an instance of the type you're extending. It's unfortunate; I'm not sure why it's required...
You can't add static methods to a type. You can only add (pseudo-)instance methods to an instance of a type.
The point of the this modifier is to tell the C# compiler to pass the instance on the left-side of the . as the first parameter of the static/extension method.
In the case of adding static methods to a type, there is no instance to pass for the first parameter.
As for extension methods, extension methods themselves are static; but they are invoked as if they are instance methods. Since a static class is not instantiable, you would never have an instance of the class to invoke an extension method from. For this reason the compiler does not allow extension methods to be defined for static classes.
Mr. Obnoxious wrote: "As any advanced .NET developer knows, new T() is slow because it generates a call to System.Activator which uses reflection to get the default constructor before calling it".
New() is compiled to the IL "newobj" instruction if the type is known at compile time. Newobj takes a constructor for direct invocation. Calls to System.Activator.CreateInstance() compile to the IL "call" instruction to invoke System.Activator.CreateInstance(). New() when used against generic types will result in a call to System.Activator.CreateInstance(). The post by Mr. Obnoxious was unclear on this point... and well, obnoxious.
This code:
System.Collections.ArrayList _al = new System.Collections.ArrayList();
System.Collections.ArrayList _al2 = (System.Collections.ArrayList)System.Activator.CreateInstance(typeof(System.Collections.ArrayList));
produces this IL:
.locals init ([0] class [mscorlib]System.Collections.ArrayList _al,
[1] class [mscorlib]System.Collections.ArrayList _al2)
IL_0001: newobj instance void [mscorlib]System.Collections.ArrayList::.ctor()
IL_0006: stloc.0
IL_0007: ldtoken [mscorlib]System.Collections.ArrayList
IL_000c: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
IL_0011: call object [mscorlib]System.Activator::CreateInstance(class [mscorlib]System.Type)
IL_0016: castclass [mscorlib]System.Collections.ArrayList
IL_001b: stloc.1
I tried to do this with System.Environment back when I was learning extension methods and was not successful. The reason is, as others mention, because extension methods require an instance of the class.
It is not possible to write an extension method, however it is possible to mimic the behaviour you are asking for.
using FooConsole = System.Console;
public static class Console
{
public static void WriteBlueLine(string text)
{
FooConsole.ForegroundColor = ConsoleColor.Blue;
FooConsole.WriteLine(text);
FooConsole.ResetColor();
}
}
This will allow you to call Console.WriteBlueLine(fooText) in other classes. If the other classes want access to the other static functions of Console, they will have to be explicitly referenced through their namespace.
You can always add all of the methods in to the replacement class if you want to have all of them in one place.
So you would have something like
using FooConsole = System.Console;
public static class Console
{
public static void WriteBlueLine(string text)
{
FooConsole.ForegroundColor = ConsoleColor.Blue;
FooConsole.WriteLine(text);
FooConsole.ResetColor();
}
public static void WriteLine(string text)
{
FooConsole.WriteLine(text);
}
...etc.
}
This would provide the kind of behaviour you are looking for.
*Note Console will have to be added through the namespace that you put it in.
The following was rejected as an edit to tvanfosson's answer. I was asked to contribute it as my own answer. I used his suggestion and finished the implementation of a ConfigurationManager wrapper. In principle I simply filled out the ... in tvanfosson's answer.
No. Extension methods require an instance of an object. You can
however, write a static wrapper around the ConfigurationManager
interface. If you implement the wrapper, you don't need an extension
method since you can just add the method directly.
public static class ConfigurationManagerWrapper
{
public static NameValueCollection AppSettings
{
get { return ConfigurationManager.AppSettings; }
}
public static ConnectionStringSettingsCollection ConnectionStrings
{
get { return ConfigurationManager.ConnectionStrings; }
}
public static object GetSection(string sectionName)
{
return ConfigurationManager.GetSection(sectionName);
}
public static Configuration OpenExeConfiguration(string exePath)
{
return ConfigurationManager.OpenExeConfiguration(exePath);
}
public static Configuration OpenMachineConfiguration()
{
return ConfigurationManager.OpenMachineConfiguration();
}
public static Configuration OpenMappedExeConfiguration(ExeConfigurationFileMap fileMap, ConfigurationUserLevel userLevel)
{
return ConfigurationManager.OpenMappedExeConfiguration(fileMap, userLevel);
}
public static Configuration OpenMappedMachineConfiguration(ConfigurationFileMap fileMap)
{
return ConfigurationManager.OpenMappedMachineConfiguration(fileMap);
}
public static void RefreshSection(string sectionName)
{
ConfigurationManager.RefreshSection(sectionName);
}
}
yes, in a limited sense.
public class DataSet : System.Data.DataSet
{
public static void SpecialMethod() { }
}
This works but Console doesn't because it's static.
public static class Console
{
public static void WriteLine(String x)
{ System.Console.WriteLine(x); }
public static void WriteBlueLine(String x)
{
System.Console.ForegroundColor = ConsoleColor.Blue;
System.Console.Write(.x);
}
}
This works because as long as it's not on the same namespace. The problem is that you have to write a proxy static method for every method that System.Console have. It's not necessarily a bad thing as you can add something like this:
public static void WriteLine(String x)
{ System.Console.WriteLine(x.Replace("Fck","****")); }
or
public static void WriteLine(String x)
{
System.Console.ForegroundColor = ConsoleColor.Blue;
System.Console.WriteLine(x);
}
The way it works is that you hook something into the standard WriteLine. It could be a line count or bad word filter or whatever. Whenever you just specify Console in your namespace say WebProject1 and import the namespace System, WebProject1.Console will be chosen over System.Console as default for those classes in namespace WebProject1. So this code will turn all the Console.WriteLine calls into blue insofar as you never specified System.Console.WriteLine.
You can use a cast on null to make it work.
public static class YoutTypeExtensionExample
{
public static void Example()
{
((YourType)null).ExtensionMethod();
}
}
The extension:
public static class YourTypeExtension
{
public static void ExtensionMethod(this YourType x) { }
}
YourType:
public class YourType { }
Although the methods of Console are static, its static methods Write() and WriteLine() merely redirect the call to Console.Out.Write() and Console.Out.WriteLine() respectively. Out is an instance whose type derives from the abstract class TextWriter. This makes it possible to define extension methods for TextWriter:
public static class ConsoleTextWriterExtensions
{
public static void WriteBlueLine(this TextWriter writer, string text)
{
Console.ForegroundColor = ConsoleColor.Blue;
writer.WriteLine(text);
Console.ResetColor();
}
public static void WriteUppercase(this TextWriter writer, string text)
{
writer.Write(text.ToUpper());
}
}
The method can then be invoked like this:
Console.Out.WriteBlueLine();
And the best part is that the type of the standard error stream instance Console.Error also derives from TextWriter which makes the same extension method also usable for Console.Error:
Console.Error.WriteBlueLine();
This can be quite useful if you have defined an extension method like WriteTable()(for writing a table out to the console) because you can also use it for the error stream or any other object of TextWriter.
Newer versions of C# allow this to be even shorter with a using static statement for Console to get red of the Console. prefix:
using static System.Console;
Out.WriteBlueLine("A blue line");
Error.WriteBlueLine("A blue line");
unfotunately NO, you CANNOT extend static classes
https://onecompiler.com/csharp/3xvbe7axg
using System;
namespace HelloWorld
{
public static class console_extensions {
public static void EXTENSION(this object item) {
System.Console.WriteLine("HELLO THERE!");
}
}
public class Program
{
public static void Main(string[] args)
{
Console.WriteLine("Hello, World!");
Console.EXTENSION();
((Console)null).EXTENSION();
Console l = new Console();
l.EXTENSION();
}
}
}
output
Compilation failed: 4 error(s), 0 warnings
HelloWorld.cs(16,12): error CS0117: `System.Console' does not contain a definition for `EXTENSION'
/usr/lib/mono/4.5/mscorlib.dll (Location of the symbol related to previous error)
HelloWorld.cs(17,5): error CS0716: Cannot convert to static type `System.Console'
HelloWorld.cs(18,4): error CS0723: `l': cannot declare variables of static types
/usr/lib/mono/4.5/mscorlib.dll (Location of the symbol related to previous error)
HelloWorld.cs(18,16): error CS0712: Cannot create an instance of the static class `System.Console'
/usr/lib/mono/4.5/mscorlib.dll (Location of the symbol related to previous error)
however you CAN pass null to the extension method
using System;
namespace HelloWorld
{
public static class static_extensions {
public static void print(this object item, int data = 0) {
Console.WriteLine("EXT: I AM A STATIC EXTENSION!");
Console.WriteLine("EXT: MY ITEM IS: " + item);
Console.WriteLine("EXT: MY DATA IS: " + data);
string i;
if (item == null) {
i = "null";
} else {
i = item.GetType().Name;
}
Console.WriteLine("EXT: MY TYPE IS: " + i + "\n");
}
}
public class Program
{
public static void Main(string[] args)
{
// an extension method can be
// called directly
// (null is an instance)
static_extensions.print(null);
// an extension method can also be
// called directly with arguments
// (null is an instance)
static_extensions.print(null, 1);
// an extension method can also be
// called as part of an instance
int x = 0; // initialize int
x.print();
// an extension method can also be
// called as part of an instance
// and with data
int x2 = 0; // initialize int
x2.print(2);
// an extension method can also be
// called directly from null
// since `null` is an instance
((string)null).print();
// an extension method can also be
// called directly from null
// and with data
// since `null` is an instance
((string)null).print(4);
}
}
}
live example: https://onecompiler.com/csharp/3xvbc8s6w
output:
EXT: I AM A STATIC EXTENSION!
EXT: MY ITEM IS:
EXT: MY DATA IS: 0
EXT: MY TYPE IS: null
EXT: I AM A STATIC EXTENSION!
EXT: MY ITEM IS:
EXT: MY DATA IS: 1
EXT: MY TYPE IS: null
EXT: I AM A STATIC EXTENSION!
EXT: MY ITEM IS: 0
EXT: MY DATA IS: 0
EXT: MY TYPE IS: Int32
EXT: I AM A STATIC EXTENSION!
EXT: MY ITEM IS: 0
EXT: MY DATA IS: 2
EXT: MY TYPE IS: Int32
EXT: I AM A STATIC EXTENSION!
EXT: MY ITEM IS:
EXT: MY DATA IS: 0
EXT: MY TYPE IS: null
EXT: I AM A STATIC EXTENSION!
EXT: MY ITEM IS:
EXT: MY DATA IS: 4
EXT: MY TYPE IS: null
I don't really get what people think they'd gain from being able to extend static classes...
What exactly would you be sacrificing by simply doing something like this?
public static class MyConsole
{
public static void WriteBlueLine(string text)
{
Console.ForegroundColor = ConsoleColor.Blue;
Console.WriteLine(text);
Console.ResetColor();
}
}
//...
MyConsole.WriteBlueLine("I'm so blue...");
Console.WriteLine("...and I'm not.");
It's minimal extra typing effort and as a bonus, it keeps things transparent...
After all, even a regular extension method is just a shorthand for a helper method. It doesn't allow you to do anything to/with a class (instance) that you wouldn't be able to do from a regular method.
You CAN do this if you are willing to "frig" it a little by making a variable of the static class and assigning it to null. However, this method would not be available to static calls on the class, so not sure how much use it would be:
Console myConsole = null;
myConsole.WriteBlueLine("my blue line");
public static class Helpers {
public static void WriteBlueLine(this Console c, string text)
{
Console.ForegroundColor = ConsoleColor.Blue;
Console.WriteLine(text);
Console.ResetColor();
}
}

Categories

Resources