Freigeben über


What's New in the BCL in .NET 4 Beta 2 [Justin Van Patten]

Visual Studio 2010 and .NET Framework 4 Beta 2 are now available to download.  .NET 4 Beta 2 contains several new BCL features and enhancements in addition to what was included in .NET 4 Beta 1.  Many of these improvements were added in large part due to specific feedback and suggestions reported by customers through Microsoft Connect.

Summary of New BCL Functionality in .NET 4 Beta 2

  • Complex Number
  • Location
  • IObservable<T>
  • Stream.CopyTo
  • Guid.TryParse, Version.TryParse, and Enum.TryParse<T>
  • Enum.HasFlag
  • String.Concat and String.Join overloads that take IEnumerable<T>
  • String.IsNullOrWhiteSpace
  • Environment.SpecialFolder additions
  • Environment.Is64BitProcess and Environment.Is64BitOperatingSystem
  • Path.Combine params support
  • TimeSpan Globalized Formatting and Parsing
  • Stopwatch.Restart
  • StringBuilder.Clear
  • IntPtr and UIntPtr Addition and Subtraction operators
  • ServiceInstaller.DelayedAutoStart
  • ObservableCollection<T> moved to System.dll

Complex Number

System.Numerics.Complex represents a complex number comprised of a real number and imaginary number.  Complex supports both rectangular coordinates (real and imaginary) and polar coordinates (magnitude and phase measured in radians) and supports arithmetic and trigonometric operations.  Complex numbers are used in a number of areas including high-performance computing, graphing and charting, electrical engineering (e.g. Fourier transforms), and other numerical applications.

Location

System.Device.Location (which can be found in System.Device.dll) enables .NET applications running on Windows 7 to determine the current geo-location (e.g. latitude/longitude) of the device.  Windows 7 supports a number of different location sensors, including GPS devices and WWAN radios, and automatically handles transitioning between different sensors — if multiple sensors are available — to provide the most accurate data for the current situation. .NET applications will be able to use this new API to access the following data (if available): latitude, longitude, altitude, horizontal and vertical accuracy, course, speed, and civic address (i.e. country/region, state/province, city, postal code, street, building, floor level).  Note that we are planning to make some changes to the shape of the location API between Beta 2 and RC, so please keep this in mind as you evaluate and use the Beta 2 location API in apps.  A separate blog post will provide more details on the planned changes for RC.

IObservable<T>

System.IObservable<T> and System.IObserver<T> provide a general mechanism for push-based notifications and can be used to represent push-based, or observable, collections.  Anyone familiar with design patterns will recognize these interfaces as representations of the well-known observer pattern.  IObservable<T> opens up the doors to some exciting new paradigms for event-based and asynchronous programming on .NET.  A great example of this is the Reactive Framework (RX), which is a library of extension methods (not included as part of .NET 4) that implement the LINQ Standard Query Operators and other useful stream transformation functions for IObservable<T>.  F# first-class events also support IObservable<T> and F# includes a library of Observable operators similar to those available in RX.  Imagine being able to do LINQ-style queries against events; this is just one of the kinds of things that IObservable<T> enables.  To help grasp the power of IObservable<T>, I encourage you to watch the Channel 9 videos of Erik Meijer introducing RX and Wes Dyer and Kim Hamilton discussing IObservable<T> in the BCL.

You may be wondering if IObservable<T> is meant to replace events in .NET.  The answer is largely “no,” but you certainly could use it in place of events if you wanted to.  Events are a well-known mechanism for notification in .NET and are already familiar to .NET developers.  As such, they will continue to be the primary mechanism for notification that we recommend for .NET APIs.  The great thing about IObservable<T> is that it is fairly straightforward for callers to convert any existing event into an IObservable<T>.  RX provides built-in methods that do this conversion and F# has support for this built-in to the language.  Stay tuned to this blog for more news about RX in the future.

Stream.CopyTo

When working with Streams, how many times have you had to write the following boiler-plate code to read from one stream and write the contents to another stream?

 Stream source = ...;
Stream destination = ...;
byte[] buffer = new byte[4096];
int read;
while ((read = source.Read(buffer, 0, buffer.Length)) != 0) {
    destination.Write(buffer, 0, read);
}

In .NET 4, you no longer have to write this code.  We’ve added a new CopyTo method to Stream that allows you to simply write:

 Stream source = ...;
Stream destination = ...;
source.CopyTo(destination);

Guid.TryParse, Version.TryParse, and Enum.TryParse<T>

We’ve added TryParse to System.Guid, System.Version, and System.Enum.  Enum.TryParse is generic, a nice improvement over the existing non-generic Parse method, which leads to much cleaner code.

On previous versions of .NET, to parse an enum value, you’d write something like this:

 try {
    string value = Console.ReadLine();
    FileOptions fo = (FileOptions)Enum.Parse(typeof(FileOptions), value);
    // Success
}
catch {
    // Error
}

On .NET 4, you can use the new generic TryParse method:

 string value = Console.ReadLine();
FileOptions fo;
if (Enum.TryParse(value, out fo)) {
    // Success
}

Enum.HasFlag

We’ve added a new convenience method to System.Enum that makes it super easy to determine if a flag is set on a particular Flags enum, without having to remember how to use the bitwise operators.  This also allows for more readable code.

In VB:

 Dim cm As ConsoleModifiers = ConsoleModifiers.Alt Or ConsoleModifiers.Shift
Dim hasAlt1 As Boolean = (cm And ConsoleModifiers.Alt) = ConsoleModifiers.Alt ' using bitwise operators
Dim hasAlt2 As Boolean = cm.HasFlag(ConsoleModifiers.Alt) ' using HasFlag

In C#:

 ConsoleModifiers cm = ConsoleModifiers.Alt | ConsoleModifiers.Shift;
bool hasAlt1 = (cm & ConsoleModifiers.Alt) == ConsoleModifiers.Alt; // using bitwise operators
bool hasAlt2 = cm.HasFlag(ConsoleModifiers.Alt); // using HasFlag

String.Concat and String.Join overloads that take IEnumerable<T>

We’ve added new overloads of String.Concat and String.Join that take IEnumerable<T>, adding to the existing overloads that take arrays.  This means you can pass any collection that implements IEnumerable<T> to these APIs without having to first convert the collection into an array.  We’ve also added “params” support to the existing array-based String.Join overload, which allows you to write more succinct code.  In C#:

 String.Join(", ", new string[] { "A", "B", "C", "D" }); // you used to have to write this
String.Join(", ", "A", "B", "C", "D"); // now you can write this

String.IsNullOrWhiteSpace

This new convenience method is similar to the existing IsNullOrEmpty method, but in addition to checking if the string is null or empty, it also checks to see if the string only consists of white-space characters, as defined by Char.IsWhiteSpace.  It works great for Code Contract preconditions.

Environment.SpecialFolder additions

We’ve added a number of new values to the Environment.SpecialFolder enum:

  • AdminTools
  • CDBurning
  • CommonAdminTools
  • CommonDesktopDirectory
  • CommonDocuments
  • CommonMusic
  • CommonOemLinks
  • CommonPictures
  • CommonProgramFilesX86
  • CommonPrograms
  • CommonStartMenu
  • CommonStartup
  • CommonTemplates
  • CommonVideos
  • Fonts
  • LocalizedResources
  • MyVideos
  • NetworkShortcuts
  • PrinterShortcuts
  • ProgramFilesX86
  • Resources
  • SystemX86
  • Templates
  • UserProfile
  • Windows

We’ve also added a new overload to Environment.GetFolderPath that takes a new SpecialFolderOption enum, allowing you to specify some additional options, such as “Create” (to create the directory if it doesn’t exist) and “DoNotVerify” (to just return the path without checking if it exists, which can help reduce lag time if the directory is on a network share).

Environment.Is64BitProcess and Environment.Is64BitOperatingSystem

These new convenience APIs make it easy to determine the bitness of the current process and operating system.  Environment.Is64BitProcess is equivalent to calling IntPtr.Size == 8.  Environment.Is64BitOperatingSystem will tell you if the underlying OS is 64-bit.  Note that it is possible for Environment.Is64BitProcess to return false and Environment.Is64BitOperatingSystem to return true if the process is a 32-bit process running on a 64-bit operating system under WOW64.

Path.Combine params support

Have you ever had to nest calls to Path.Combine like this:

 Path.Combine("dir1", Path.Combine("dir2", Path.Combine("dir3", "dir4")));

With .NET 4, you can simply write:

 Path.Combine("dir1", "dir2", "dir3", "dir4");

TimeSpan Globalized Formatting and Parsing

In previous versions of .NET, TimeSpan only supported a single culture-agnostic format for formatting and parsing.  This meant if you called ToString on a French machine the output would not match culture settings, making the application appear not to be globalized.  This is often noticeable in apps that display results from SQL databases that use the frequently used Time column type, because SQL’s Time maps to TimeSpan in .NET.  For example:

 Thread.CurrentThread.CurrentCulture = new CultureInfo("fr-FR");
Console.WriteLine(new TimeSpan(0, 1, 1, 1, 1));
Console.WriteLine(12.34);

Output:

 01:01:01.0010000
12,34

The output from TimeSpan.ToString is a culture-agnostic format so it uses a '.' as the decimal separator, whereas Double uses ',' based on the current culture.

In .NET 4, System.TimeSpan now supports formatting and parsing based on culture settings via new overloads of ToString, Parse, and TryParse, and new ParseExact and TryParseExact methods.  The default ToString overload still behaves the same for backwards compatibility, outputting a constant, culture-agnostic format, but the new overloads allow you to specify additional culture-sensitive formats.  You can pass the “g” format (general format) to the new ToString overload to specify a culture-sensitive format:

 Thread.CurrentThread.CurrentCulture = new CultureInfo("fr-FR");
Console.WriteLine(new TimeSpan(0, 1, 1, 1, 1).ToString("g"));
Console.WriteLine(12.34);

Output:

 01:01:01,0010000
12,34

With this new format, the output from TimeSpan is in line with the user’s culture settings.

Stopwatch.Restart

This is a new convenience method that is equivalent to calling Stopwatch.Reset followed by Stopwatch.Start.

StringBuilder.Clear

In previous versions of .NET, you could clear StringBuilder by setting the Length property to 0.  This isn’t very discoverable, so we’ve added StringBuilder.Clear as a more discoverable equivalent.

IntPtr and UIntPtr Addition and Subtraction operators

Adding an offset to an IntPtr or UIntPtr can be error prone and often introduces subtle bugs on 64-bit machines if not done correctly.  .NET 4 includes addition and subtraction operators on these types, along with Add and Subtract methods, to help prevent these kinds of errors.

ServiceInstaller.DelayedAutoStart

.NET-based services that start automatically can now set a new DelayedAutoStart property to true (recommended), which will delay auto-starting the service until after other auto-start services are started plus a short delay to improve system boot performance when running on Vista or greater.

ObservableCollection<T> moved to System.dll

We’ve type-forward System.Collections.ObjectModel.ObservableCollection<T>, System.Collections.ObjectModel.ReadOnlyObservableCollection<T>, and System.Collections.Specialized.INotifyCollectionChanged from WindowsBase.dll (a WPF assembly) to System.dll.  This allows you to use these collections without taking a dependency on a WPF assembly and enables a cleaner separation between model and view.

We hope you like what we’ve added to the BCL in .NET 4 Beta 2.  Download Visual Studio 2010 and .NET 4 Beta 2 today.  Be sure to let us know what you think and report any bugs you run into.

Update: Added links to the MSDN documentation for each feature throughout. We'd appreciate any feedback on the docs as well. Also updated ServiceInstaller with the correct name (fixed from ServiceProcessInstaller).

Comments

  • Anonymous
    October 21, 2009
    Thanks for the great overview.

  • Anonymous
    October 21, 2009
    Excellent overview.  Minor tweaks like these go a long way to improving readability and reducing pain.  The new Path.Combine overload is definitely handy - long path combinations quickly become ugly. I also like the bitwise HasFlag helper.  Bitwise checks are pretty rare, but whenever they do come up I always have to double check my operators to make sure I'm checking the flag correctly (and then still feel unsure about the result).

  • Anonymous
    October 21, 2009
    These additions are awesome. I've been slightly annoyed by at least half of these. I'm glad you guys not only add new big features, but also add small refinements to take away small annoyances.

  • Anonymous
    October 21, 2009
    complex number,cool! What about SIMD vector support:)

  • Anonymous
    October 21, 2009
    regarding the ServiceProcessInstaller.DelayedAutoStart property. You say 'which will delay auto-starting the service until after other auto-start services are started' What happens if there are two services with ServiceProcessInstaller.DelayedAutoStart =true?

  • Anonymous
    October 21, 2009
    I wish string.Intern(null) simply returned null instead of throwing an ArgumentNullException. It's annoying to have to write:  if (s != null) s = string.Intern(s); or  some_method(s != null ? string.Intern(s) : null); string.Intern() should do its best to replace identical strings with one canonical instance but nothing more. If it can't find a match, it returns the original string. 'null' won't match anything so it should be returned unmodified too. It would be nice to be able to flush the intern pool programmatically too (get rid of the unreferenced strings). Even string.Unintern(s) might help in specific cases.

  • Anonymous
    October 21, 2009
    Nice, A implementation of Quaternions would be of interest since they are very usefull. Especially in vector math. Arbitrary sized matrices(Maybe with some type of template expansion for perf), or more advanced mathlib with interchangable implementation,(LINQ/MEF architecture). Handling of CMYK/ or colorspaces in graphics?

  • Anonymous
    October 21, 2009
    I've had my own Enum.TryParse<T> for a long time now - Thanks for adding it. How about adding compiler support that will generate  Enum.TryParse(value, out fo) when I write  FileOptions.TryParse(value,out fo) Just much more readable

  • Anonymous
    October 21, 2009
    Hi Justin, Thanks for the great overview! These are many very good additions, my favorite one being Enum.HasFlag as I have to do a lot of interop stuff ;) Do you think there is a chance to also get something like T Enum.SetFlag<T>(T flag, bool set) {    if (set)    {        return this | flag;    }    else    {        return this & ~flag;    } } ? That would be very cool as checking for a flag is usually the easier operation compared to selectively setting and clearing flags which always requires an if and two bit modification statements. Thanks, Thomas

  • Anonymous
    October 21, 2009
    All great stuff, long overdue and will be very useful going forward. I wish the .NET Framework team as a whole would concentrate more on the "little" things like this that actually make a big difference in every day programming. I do have a question though: why do the TryParse methods use an out variable, instead of returning a nullable result? The syntax for out variables is awkward, and the Framework Design Guidelines specifically state to avoid them for that reason. For reference types you could just return null (as far as I know, null is never a valid result of a Parse operation), and for value types you could return a nullable version. This would make using these TryParse methods much simpler. Although I imagine it is too late to change them now.

  • Anonymous
    October 22, 2009
    Very cool. Stream.CopyTo(), Enum.TryParse(), Enum.HasFlag() and Path.Combine() are my favorite in this list, probably because they're not the end of the world to implement on my own, it just makes my code more readable to have these available natively. One question, and I'm not suggesting a name change, but is String.IsNullOrWhiteSpace actually IsNullOrEmptyOrWhiteSpace?

  • Anonymous
    October 22, 2009
    David Nelson, you want the out param so that you can inline the call in an If statement. Otherwise you'd have to create a variable, assign the result to it and then check it for null or worse, make two calls to TryParse, the first checking for not null and the second to get the value. Since TryParse is a convenience method intended to help make code more readable the out param version makes the most sense.

  • Anonymous
    October 22, 2009
    @Eyal > How about adding compiler support that will generate > Enum.TryParse(value, out fo) > when I write > FileOptions.TryParse(value,out fo) Since TryParse is a static method, and any enum type derives from System.Enum, it's already precisely how it works with no special compiler support.

  • Anonymous
    October 22, 2009
    Generic Enum.TryParse is great, but what about providing generic overloads for existing Enum.Parse and Enum.IsDefined? The inconsistency between TryParse and Parse looks is especially glaring.

  • Anonymous
    October 22, 2009
    What is the buffer size used in the Stream.CopyTo functionality? I'm guessing that isn't configurable.

  • Anonymous
    October 22, 2009
    Chris Haas, you are assuming that I want to inline the call into an If statement. Often when I do this kind of parsing, I am simply storing the value for use later on. In that case a nullable value is exactly what I want. Compare: var maybeADay = Enum.TryParse<DayOfWeek>("Tuesday"); with: DayOfWeek? maybeADay; if(!Enum.TryParse("Tuesday", out maybeADay))   maybeADay = null; On the other hand, if I do want to use the value in an If statement, compare: DayOfWeek maybeADay; if(Enum.TryParse("Tuesday", out maybeADay))   Console.WriteLine("Tuesday is a day of the week."); with: var maybeADay = Enum.TryParse<DayOfWeek>("Tuesday"); if(null != maybeADay)   Console.WriteLine("Tuesday is a day of the week."); It seems obvious to me that the difference in readability between the first set is much greater than that of the second set. That is why I would have much preferred that TryParse use nullable values instead of out variables.

  • Anonymous
    October 22, 2009
    I think TryParse is fine as it is. Consider this: TryParse is a single established pattern for "attempted parse" for all kinds of types. It couldn't use null as a failure marker, because it is not restricted to value types, and for reference types null may be a legal result of a parse. And, IMO, having different convention for TryParse for value and reference types would be more trouble than it's worth. At least the way it is, I readily know how to work with it regardless of the type in question - so long as it has a static member named "TryParse"...

  • Anonymous
    October 22, 2009
    I'm happy to see my numerous requests for Enum.TryParse and Guid.TryParse made it to the BCL. As stated in previous comments, it's these little things in the BCL that (also) are true gems of BCL 4.0. Make sure to take notice of all the comments on this topic in particular and keep it in mind when planning for BCL v.next. Matt: I see you haven't checked with BCL 4.0 beta 2. There's an overload on Stream.CopyTo() that takes the buffer parameter you're looking for - I guess they figured you'd ask already :) A valid point with the String.IsNullOrWhiteSpace actually being IsNullOrEmptyOrWhiteSpace. Is a name change plausible?

  • Anonymous
    October 22, 2009
    Pavel, under what circumstances would null be a valid result of a parse operation, even for a reference type? Sadly, since the versions of TryParse that already exist in the framework use out variables, changing the pattern now would probably cause confusion. I would have preferred that they all use nullable values from the beginning, but I suppose changing it now would be more trouble than its worth.

  • Anonymous
    October 22, 2009
    David, it's mostly about having the same concept as other APIs in the .NET framework, i.e. consistency. For instance compare the TryParse methods with the TryGetValue ones. Plus, for the BCL a goal is to possibly also be consistent to future code, which includes the code of customers. Take the following example: I have a custom data type (doesn't matter whether it's value or reference type). That type specifies that it can parse itself from a string like an integer does, except that an empty string is legal and will be mapped to null. For short: "-1"  -->  -1 "0"   -->  0 "1"   -->  1 ""    -->  null (legal) "ab"  -->  null (illegal) In this case I would definitely have to declare my own TryParse method with an out parameter, otherwise there's no way to tell the difference from a legal null result and an illegal one. Now, it's goodness because my method has the same signature as most other types in the .NET space (also remember: I have no other choice than to use an out parameter). But, if the BCL changed the signatures to the way you propose it to work, then my TryParse method would have a different signature, no matter how you put it. Thus, to be compatible with most use cases possible and to be consistent with the solutions of other similar problems, the current API is a good choice. Although I have to admit that I also find your code clearly better to read.

  • Anonymous
    October 22, 2009
    Ooh, the BCL cannot possibly anticipate every variation of user code that may resemble some pattern they have already established in the framework. The minor inconsistency that hypothetically might arise from a few parsing corner cases is not nearly as important as the loss of usability and readability that definitely results from using out parameters in a large majority of cases. IMHO.

  • Anonymous
    October 22, 2009
    Rx Framework will not be part of the .NET 4 RTM ?? Why?

  • Anonymous
    October 23, 2009
    Hi, I've been thinking about the String.IsNullOrWhiteSpace() method. What about overloading the existing String.IsNullOrEmpty with a boolean argument that determines whether to compare for white space? String.IsNullOrEmpty(string s, bool trimWhiteSpace);

  • Anonymous
    October 23, 2009
    The comment has been removed

  • Anonymous
    October 25, 2009
    Hello. Why there is still no way to use Func<T, TResult> type in opereator definition?! What if I want to override implicit cast operator to convert Func<T, TResult> argument to the instance of my class? Why there is no ICloneable<T>? What about generic type operator constraints? There is still no way to do Add(T1 a, T2 b){return a + b;}?

  • Anonymous
    October 25, 2009
    Would have preferred an overload to String.IsNullOrEmpty(String str, Boolean ignoreWhiteSpace)

  • Anonymous
    October 25, 2009
    Great new features. But i think it would be better to return an integer value tat says the bit of the OS rather than have this method Is64BitOperatingSystem() coz from the news i hear Microsoft is working on 128bit, so everytime the architecture changes, we need to upgrade our codes. mite be it would be better to have like GetBitOfOS() or something like tat which is more general. after all .net code is also about portabitlity.

  • Anonymous
    October 26, 2009
    The comment has been removed

  • Anonymous
    October 28, 2009
    The reason an out parameter is used rather than a nullable return value is that the TryParse method itself pre-dates nullable types. E.g. one of the TryParse overloads for Double was available in .NET 1.0 whereas nullable types were only introduced in .NET 2.0. While it's true that a lot more TryParse methods were added in .NET 2.0 it's also true that many .NET developers were already familiar with the existing .NET 1.0 pattern. Also note that what is frequently referred to as the TryParse pattern is actually the TryX pattern because it is a common alternative to the tester-doer pattern. So while a null value may not make sense for parsing, the framework pattern itself is not limited to parsing. E.g. the TryGetValue method on the generic dictionary is another example of the TryX pattern, but where null would be valid.

  • Anonymous
    October 28, 2009
    @Pavel Minaev [MSFT] I meant non-specified generic types. I cannot just say "operator +<T> (int a, T b)". Here is a more practical example: public abstract class Provider<T> {  static public implicit operator Provider<T>(Func<T> value) { //works    return new Function<T>(value);  }  static public implicit operator Provider<T>(Func<T1, T> value) { //doesn't work    return new Function<T1, T>(value);  } }

  • Anonymous
    October 31, 2009
    IObservable<T>.Subscribe Good stuff.  How does an IObserver<T> tell the IObservable<T> that it doesnt want to subscribe anymore?  And how is the whole relationship cleaned up? Thanks Paul

  • Anonymous
    November 08, 2009
    Hello, Could you explain what is the rationale for implementing Equals/GetHashCode on things like ConstructorInfo?