Print service provided by iDogiCat: http://www.idogicat.com/
home logo





Home > IT > Programming > C# > C# Notes

C# Notes

debug: check data in ~DataTable

data_table | Rows | Results View | 0, .., n (row data) | Item Array | 0, .., n (col data)

debug: check columns in ~DataTable

data_table | Columns | Results View

Log4net

Sometimes it seems that log4net won't work... A solution from internet search is as follows:

  • App.config (this seems to be copied to <my-app-name.exe.config> after building the solution

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" />
  </configSections>

  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
  </startup>

  <appSettings>
    <add key="log4net.config" value="log4net.config" />
  </appSettings>
</configuration>

  • add the log4net.config file
<log4net>
  <appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
    <file type="log4net.Util.PatternString" value="mylog.log" />
    <appendToFile value="true"/>
    <rollingStyle value="Size"/>
    <maximumFileSize value="20MB"/>
    <maxSizeRollBackups value="10"/>
    <layout type="log4net.Layout.PatternLayout">
      <conversionPattern value="%utcdate [%4thread] %-5level %logger - %message%newline"/>
    </layout>
  </appender>
  <root>
    <level value="INFO"/>
    <appender-ref ref="RollingFileAppender"/>
  </root>
</log4net>

Other log4net examples:

<log4net>
   <appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
      <file value="RollingLog.log" />
      <appendToFile value="true" />
      <rollingStyle value="Size" />
      <maxSizeRollBackups value="5" />
      <maximumFileSize value="10MB" />
      <staticLogFileName value="true" />
      <layout type="log4net.Layout.PatternLayout">
         <conversionPattern value="%date{dd MMM yyyy HH:mm:ss} (%level) %logger %message%newline" />
      </layout>
   </appender>
   <appender name="SmtpAppender" type="log4net.Appender.SmtpAppender">
      <to value="errors@me.com" />
      <from value="log@logmail.log" />
      <subject value="Log message" />
      <smtpHost value="MySMTPServer" />
      <bufferSize value="1024" />
      <lossy value="false" />
      <layout type="log4net.Layout.PatternLayout">
         <conversionPattern value="%message%newline%newline Logger details: %date{dd MMM yyyy HH:mm:ss} %logger%newline%newline" />
      </layout>
   </appender>
   
   <appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
       <target value="Console.Error" /> <!-- This makes it log to stderr. Removing this then it logs to stdout. -->
       <layout type="log4net.Layout.PatternLayout">
           <conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" />
       </layout>
   </appender>
   
   <!--ALL DEBUG INFO WARN ERROR FATAL OFF-->
   <root>
      <level value="OFF" />
   </root>
   
   <logger name="Log4NetExamples.ConsoleApplication1">
      <level value="INFO" />
      <appender-ref ref="RollingFileAppender" />
   </logger>
   
   <logger name="EmailLogger">
      <level value="ERROR" />
      <appender-ref ref="SmtpAppender" />
   </logger>
</log4net>

And more examples (log to DB, etc.): https://logging.apache.org/log4net/release/config-examples.html

Using

Using is the same as Try ... Finally, it grantees to dispose an object explicitly (by calling its Dispose() function).

Using obj As New MyObject
    ' code to use 'obj' variable
End Using

The above is the same as following code:

Dim obj As New MyObject

Try
    ' code to use 'obj' variable
Finally
    If obj IsNot Nothing Then
        obj.Dispose()
    End If
End Try

When using socket related stuffs in WCF, if you don't explicitly close the socket after using it and relying on the Garbage Collection and its Dispose() function, when you need to create a lot of sockets, you can run out of the quota, and get following exception:

System.TimeoutException: The open operation did not complete within the allotted timeout of 00:01:00. The time allotted to this operation may have been a portion of a longer timeout. ---> System.TimeoutException: The socket transfer timed out after 00:01:00. You have exceeded the timeout set on your binding. The time allotted to this operation

The fix is to close the socket explicitly. A convenient way to do this is using the above 'Using' feature.

C# version of using:

using (var obj = new MyObject() )
{
    // code to use 'obj' variable
}

About IDisposable.Dispose()

Basically, if a class implements IDisposable, as soon as finish using it, we should call its Dispose() function. The convenient way of doing this is using 'using'.

https://docs.microsoft.com/en-us/dotnet/api/system.idisposable.dispose?redirectedfrom=MSDN&view=netframework-4.7.2#System_IDisposable_Dispose

?? operator

The ?? operator is called the null-coalescing operator. It returns the left-hand operand if the operand is not null; otherwise it returns the right hand operand.

int? x = null;
int y = x ?? 0;   // assign 0 to y. if x is not null, then assign x to y

// a shorthand way of writing:
// int y = x == null? 0 : x;

Linq

  • Linq: by default it cannot be used. you should add the following line to use it:
using System.linq;

  • Linq and Java Stream API:
    • Select: map
      • Note: Select returns a collection, so there is no need to use something like '.collect()' as in Java.
    • Where: filter
    • combine two collections: coll1.Concat(coll2);
    • Any: findAny (any item match condition?)
    • Aggregate

List list = new List() { "aaa", "bbb", "ccc"};
list.Aggregate((a, b) => a + ", " + b));

  • (as in java) there is no way to do an action on all items in a stream. in C#, currently the way to do this is:

foreach (var item in myCollection.Where(...).Where(...))
{
    // do something on item
}

Uninitialized variables

Class member variables by default is initialized to null. But local variables won't get initialized unless they are done explicitly by programmer.

Keyword 'params'

Keyword 'params' is used to specify variable number of arguments for functions.

public class TestClass
{
    private void TestFunciton(params int[] list)
    {
        for (int i = 0; i < list.Length; ++i)
        {
            Console.Write(list[i] + " ");
        }
    }

    static void Main()
    {
        TestFunction(1);
        TestFunction(1, 2, 3, 4, 5);
        TestFunction(new int[] {1, 2, 3});    // OK but not needed
    }
}

Collections

Dictionary

this is something similar to ~HashMap in Java. But there are some differences:

  • it doesn't accept null as key (throws ArgumentNullException)
  • C# uses [] operator to access elements in it (in java, put and get methods are used)
  • duplicated key: in Java, the respective value is overwritten; in C#, it's the same if [] operator is used, but if Add() is used, it throws ArgumentException.
  • if container doesn't contain a key-value pair, in Java, calling get() with this key returns null; in C#, it throws KeyNotFoundException.
  • C#: ContainsKey() method can be used to deal with non-existing keys and duplicated keys.
  • C#: there is also TryGetValue() method:
Dictionary myDictionary = new Dictionary();
string myKey = "no-such-key";
MyObject myValue = null;

if(!myDictionary.TryGetValue(myKey, myValue)) {
    // key doesn't exist
}

Create a dictionary from a list of data by aggregating data with the same 'key':

public static void Main()
{
var dataList = new List<ProductPrice>() {
    new ProductPrice("aaa", 1),
    new ProductPrice("aaa", 2),
    new ProductPrice("aaa", 3),
    new ProductPrice("bbb", 1),
    new ProductPrice("ccc", 2)
};

// create a dictionary of IDictionary<string, List<int>>:
var dict1 = dataList.GroupBy(t => t.name).ToDictionary(g => g.Key, g => g.ToList());

// create a dictionary of IDictionary<string, int>, with the first price
var dict2 = dataList.GroupBy(t => t.name).ToDictionary(g => g.Key, g => g.First());

// create a dictionary of IDictionary<string, int>, with the highest price
var dict3 = dataList.GroupBy(t => t.name).ToDictionary(g => g.Key, g => g.ToList().OrderByDescending(v => v.price).First());

// or: do the above in this way
var dict4 = dataList.OrderByDescending(v => v.price).GroupBy(t => t.name).ToDictionary(g => g.Key, g => g.First());


foreach(var entry in dict) 
{
    System.Console.WriteLine(entry.Key + " ---> " + entry.Value);
    
    //foreach(var v in entry.Value)
    //    System.Console.WriteLine("\t" + v);
}

auto binding

This is in .Net 4.7:

var (a, b, c) = (1, 2, 3);
var (v1, v2) = a > b? (a, b) : (b, a);

async & await

http://getgoingit.blogspot.com/2017/09/using-async-await-in-c.html

http://getgoingit.blogspot.com/2017/09/using-async-await-in-different-scenarios.html

Floating point numbers

  • float: 32-bit, 7 digits. mainly used in graphic libs since performance is very important while rounding errors are usually not important.
  • double: 64-bit, 15-16 digits. Most used one for real numbers.
  • decimal: 128 bit, 28-29 significant digits. It can accurately represent any number within its precison, while float and double cannot accurately represent all numbers, even those within their precisions. Decimal type is mainly used in financial applications. It's slower than the other two types.

No primitive data type

In C# there is no primitive data type (but Java has such things like char, int, double, etc.). Everything in C# are objects. But there are some aliases to simplify the uses of some frequently used data types:

AliasFull Name
boolSystem.Boolean
byteSystem.Byte
sbyteSystem.SByte
shortSystem.Int16
ushortSystem.UInt16
intSystem.Int32
uintSystem.UInt32
longSystem.Int64
ulongSystem.UInt64
floatSystem.Single
doubleSystem.Double
decimalSystem.Decimal
charSystem.Char
stringSystem.String
objectSystem.Object

Property

getters and setters can be greatly simplified as follows:

public int MyProperty {get; set;}

One of them can also be removed in order to make it a read-only or write-only property.

Default value also can be specified:

public string Name {get; set;} = null;

Catch all exceptions and re-throw

try
{
    ...
}
catch
{
    DoCleanUps();
    throw;
}

Explicit interface implementation

When you implement an interface, all methods in interface should be made public.

In case we don't want too many users to see these methods, 'explicit interface implementation' can be used. In this case, the method can still be visible to outside, but only through the interface.

public interface MyInterface
{
    int MyFunc();
}

public class MyClass : MyInterface
{
    // usually we should do this:
    // public int MyFunc() {return 0;}
    // use explicit interface implementation:
    int MyInterface.MyFunc() {return 0;}
}

// usage
MyClass c = new MyClass();
c.MyFunc(); // error
((MyInterface)c).MyFunc(); // OK