In a large software project, it's easy to mess up units for time. IE, is that Int32 seconds or milliseconds? Or maybe minutes? I've been fixing my team's source code to use the unit-agnostic TimeSpan class wherever possible. However the .NET Framework is not complete in its adoption of TimeSpan. Specifically, we don't have a Process.WaitForExit overload that takes a TimeSpan, only an Int32 for the timeout.
I suggest someone look through all .NET Framework API's for Int32 parameters containing "second", "millisecond", "ms", "timeout" (and perhaps "time") and see if there is a parallel TimeSpan-based overload. If not, please fix that.
(Proposed by @reflectronic)
namespace System {
public static class GC {
+ public static GCNotificationStatus WaitForFullGCApproach(TimeSpan timeout);
+ public static GCNotificationStatus WaitForFullGCComplete(TimeSpan timeout);
}
}
namespace System.ComponentModel.DataAnnotations {
public class RegularExpressionAttribute {
! This one might not end up being that useful, since people generally don't ever manipulate an instance of this attribute.
+ public TimeSpan MatchTimeout { get; }
}
}
namespace System.Diagnostics {
public class Process {
+ public bool WaitForExit(TimeSpan timeout);
+ public bool WaitForInputIdle(TimeSpan timeout);
}
}
namespace System.IO {
public class FileSystemWatcher {
+ public WaitForChangedResult WaitForChanged(WatcherChangeTypes changeType, TimeSpan timeout);
}
public sealed class NamedPipeClientStream : PipeStream {
+ public void Connect(TimeSpan timeout);
+ public Task ConnectAsync(TimeSpan timeout, CancellationToken cancellationToken = default);
}
}
namespace System.Net.NetworkInformation {
public class Ping {
+ public PingReply Send(IPAddress address, TimeSpan timeout, byte[]? buffer = null, PingOptions? options = null);
+ public PingReply Send(string hostNameOrAddress, TimeSpan timeout, byte[]? buffer = null, PingOptions? options = null);
! Skipped EAP based methods; if they are desired, they can be added back in
! I added CancellationToken because it is probably worth it. If you don't want it you can remove it.
+ public Task<PingReply> SendPingAsync(IPAddress address, TimeSpan timeout, byte[]? buffer = null, PingOptions? options = null, CancellationToken cancellationToken = default);
+ public Task<PingReply> SendPingAsync(string hostNameOrAddress, TimeSpan timeout, byte[]? buffer = null, PingOptions? options = null, CancellationToken cancellationToken = default);
}
}
namespace System.Net.Sockets {
public class NetworkStream : Stream {
+ public void Close(TimeSpan timeout);
}
public class Socket {
+ public bool Poll(TimeSpan timeout, SelectMode mode);
+ public static void Select(IList checkRead, IList checkWrite, IList checkError, TimeSpan timeout);
}
}
namespace System.ServiceProcess {
public class ServiceBase {
+ public void RequestAdditionalTime(TimeSpan time);
}
}
namespace System.Threading.Tasks {
public class Task {
+ public bool Wait(TimeSpan timeout, CancellationToken cancellationToken);
}
}
namespace System.Timers {
public class Timer {
+ public Timer(TimeSpan interval);
}
}
APIs that I don't know how to make better:
namespace System.IO {
public abstract class Stream {
+ public TimeSpan ReadTimeoutTimeSpan { get; set; }
+ public TimeSpan WriteTimeoutTimeSpan { get; set; }
}
}
namespace System.IO.Ports {
public class SerialPort {
+ public TimeSpan ReadTimeoutTimeSpan { get; set; }
+ public TimeSpan WriteTimeoutTimeSpan { get; set; }
}
}
namespace System.Media {
public class SoundPlayer {
+ public TimeSpan LoadTimeoutTimeSpan { get; set; }
}
}
namespace System.Net.NetworkInformation {
+ public static class NetworkInformationTimeSpanExtensions {
+ public static TimeSpan GetPacketReassemblyTimeout(this IPGlobalStatistics statistics);
+ public static TimeSpan GetMaximumTransmissionTimeout(this TcpStatistics statistics);
+ public static TimeSpan GetMinimumTransmissionTimeout(this TcpStatistics statistics);
+ }
}
namespace System.Net.Sockets {
public class Socket {
+ public TimeSpan ReceiveTimeoutTimeSpan { get; set; }
+ public TimeSpan ReceiveTimeoutTimeSpan { get; set; }
}
public class TcpClient {
+ public TimeSpan ReceiveTimeoutTimeSpan { get; set; }
+ public TimeSpan SendTimeoutTimeSpan { get; set; }
}
}
namespace System.Timers {
public class Timer {
+ public TimeSpan IntervalTimeSpan { get; set; }
}
}
APIs which use seconds:
These suffer from the same problem as the previous group, but also only deal with seconds, meaning that most TimeSpan values would need to be rounded out.
namespace System.Data {
! This would need to be a DIM.
public interface IDbCommand {
+ TimeSpan CommandTimeoutTimeSpan { get; set; }
}
+ public static class DataTimeSpanExtensions {
+ public static TimeSpan GetConnectionTimeout(this IDbConnection connection);
+ }
}
namespace System.Data.Sql {
public sealed class SqlNotificationRequest {
+ public TimeSpan TimeoutTimeSpan { get; set; }
}
}
namespace System.Data.SqlClient {
public sealed class SqlBulkCopy {
+ public TimeSpan BulkCopyTimeoutTimeSpan { get; set; }
}
public sealed class SqlConnectionStringBuilder {
+ public TimeSpan ConnectTimeoutTimeSpan { get; set; }
+ public TimeSpan LoadBalanceTimeoutTimeSpan { get; set; }
}
public sealed class SqlDependency {
+ public SqlDependency(SqlCommand command, string options, TimeSpan timeout);
}
}
namespace System.Net.Sockets {
public class LingerOption {
+ public LingerOption(bool enable, TimeSpan time);
+ public TimeSpan LingerTimeSpan { get; set; }
}
public class Socket {
+ public void Close(TimeSpan timeout);
}
}
I personally am against doing this right now.
Not because I think the idea has no merit (on the contrary, I'm all for using a specific type that means "duration without units"), but because I'd rather do this with a type from a rebuilt date/time library (and hit all date/time related entry points, which would be a larger undertaking).
We think this is a good idea, even using the current type. What would be helpful is if someone could look at all the types we are going to expose (see https://github.com/dotnet/corefx-progress for a complete list) and produce a speclet of the APIs we'd need to add. Then we can take it into API review and start doing the work.
@ellismg The request is a compiled list of all API members included in src-full that include an indication of time in minutes, seconds, milliseconds?
I am very much a fan of this, and believe TimeSpan is the correct type to use.
I'm also a fan of this, as long as it is only implemented in places where time is used as a duration. (TimeSpan should _not_ be used when referring to a time-of-day).
There is a lot of trickery due to .NET Framework compat and how we type forward contracts.
@briangru, it would be helpful if you could distill the list of APIs that we'd need to add. Then we can break it down more.
We need list of APIs that should be added. Then we need to review them.
Time to shine some lights on this issue? :^)
System.GC.WaitForFullGCApproach(int millisecondsTimeout);
System.GC.WaitForFullGCComplete(int millisecondsTimeout);
System.Net.Sockets.Socket.Close(int timeout);
System.Net.Sockets.Socket.Poll(int microSeconds, SelectMode mode)
System.Net.Sockets.Socket.Select(IList checkRead, IList checkWrite, IList checkError, int microSeconds)
System.Net.Sockets.NetworkStream.Close(int timeout)
System.Net.Sockets.LingerOption.ctor(bool enable, int seconds)
System.Net.Sockets.LingerOption.LingerTime // int, in seconds
System.Security.Cryptography.Pkcs.Rfc3161TimestampTokenInfo.ctor(
Oid policyId,
Oid hashAlgorithmId,
ReadOnlyMemory<byte> messageHash,
ReadOnlyMemory<byte> serialNumber,
DateTimeOffset timestamp,
long? accuracyInMicroseconds = null,
bool isOrdering = false,
ReadOnlyMemory<byte>? nonce = null,
ReadOnlyMemory<byte>? tsaName = null,
X509ExtensionCollection extensions = null)
Perhaps could automate the searching by using Roslyn to look for method / constructor parameters and property name containing "time", "second" etc. (In fact, I tried it but failed miserably)
Is there any plans regarding this issue? seems abandoned considering that it's been more than two years since the last update.
/cc @joperezr
@Gnbrkm41 it is in Future milestone, we do not close valid issues when we do not have time to look at them. It may be opened for longer time than "just" 2 years.
The APIs which are new in 3.0 (e.g. LingerOption) should be changed before we ship 3.0 ... having a separate issue for that would be good.
For the rest, we can add overloads in 3.0.
Are you interested in picking it up?
@karelz Ah, right. I see.
If you're talking about the LingerOption, sure, I can open an issue regarding that problem.
@karelz looking at the .NET API catalog, it appears that LingerOption is present since .NET Framework 1.0. Are you sure it's new in Core 3.0?
@Gnbrkm41 not sure anymore. I likely mixed it up with another Linger feature.
I likely mixed it up with another Linger feature.
Maybe you meant keep alive?
Yep, that's the one ... KeepAlive. Sorry for confusion.
Okay, I have semi-automatically generated a list of types (and the specific members) that don't use a TimeSpan where it would be appropriate:
Offending Types
namespace System {
public static class GC {
public static GCNotificationStatus WaitForFullGCApproach(int millisecondsTimeout);
public static GCNotificationStatus WaitForFullGCComplete(int millisecondsTimeout);
}
}
namespace System.ComponentModel.DataAnnotations {
public class RegularExpressionAttribute {
public int MatchTimeoutInMilliseconds { get; set; }
}
}
namespace System.Data {
public interface IDbCommand {
int CommandTimeout { get; set; }
}
public interface IDbConnection {
int ConnectionTimeout { get; }
}
}
namespace System.Data.Sql {
public sealed class SqlNotificationRequest {
public int Timeout { get; set; }
}
}
namespace System.Data.SqlClient {
public sealed class SqlBulkCopy {
public int BulkCopyTimeout { get; set; }
}
public sealed class SqlConnectionStringBuilder {
public int ConnectTimeout { get; set; }
public int LoadBalanceTimeout { get; set; }
}
public sealed class SqlDependency {
public SqlDependency(SqlCommand command, string options, int timeout);
}
}
namespace System.Diagnostics {
public class Process {
public bool WaitForExit(int milliseconds);
public bool WaitForInputIdle(int milliseconds);
}
}
namespace System.IO {
public class FileSystemWatcher {
public WaitForChangedResult WaitForChanged(WatcherChangeTypes changeType, int timeout);
}
public abstract class Stream {
public virtual int ReadTimeout { get; set; }
public virtual int WriteTimeout { get; set; }
}
}
namespace System.IO.Pipes {
public sealed class NamedPipeClientStream : PipeStream {
public void Connect(int timeout);
public Task ConnectAsync(int timeout);
public Task ConnectAsync(int timeout, CancellationToken cancellationToken);
}
}
namespace System.IO.Ports {
public class SerialPort {
public int ReadTimeout { get; set; }
public int WriteTimeout { get; set; }
}
}
namespace System.Media {
public class SoundPlayer {
public int LoadTimeout { get; set; }
}
}
namespace System.Net {
public sealed class FtpWebRequest : WebRequest {
public int ReadWriteTimeout { get; set; }
}
public sealed class HttpWebRequest : WebRequest {
public int ContinueTimeout { get; set; }
public int ReadWriteTimeout { get; set; }
}
public class ServicePoint {
public int ConnectionLeaseTimeout { get; set; }
}
public class ServicePointManager {
public static int DnsRefreshTimeout { get; set; }
}
public abstract class WebRequest {
public virtual int Timeout { get; set; }
}
}
namespace System.Net.Mail {
public class SmtpClient {
public int Timeout { get; set; }
}
}
namespace System.Net.NetworkInformation {
public abstract class IPGlobalStatistics {
public abstract long PacketReassemblyTimeout { get; }
}
public class Ping {
public PingReply Send(IPAddress address, int timeout);
public PingReply Send(string hostNameOrAddress, int timeout);
public PingReply Send(IPAddress address, int timeout, byte[] buffer);
public PingReply Send(string hostNameOrAddress, int timeout, byte[] buffer);
public PingReply Send(IPAddress address, int timeout, byte[] buffer, PingOptions options);
public PingReply Send(string hostNameOrAddress, int timeout, byte[] buffer, PingOptions options);
public void SendAsync(IPAddress address, int timeout, object userToken);
public void SendAsync(string hostNameOrAddress, int timeout, object userToken);
public void SendAsync(IPAddress address, int timeout, byte[] buffer, object userToken);
public void SendAsync(string hostNameOrAddress, int timeout, byte[] buffer, object userToken);
public void SendAsync(IPAddress address, int timeout, byte[] buffer, PingOptions options, object userToken);
public void SendAsync(string hostNameOrAddress, int timeout, byte[] buffer, PingOptions options, object userToken);
public Task<PingReply> SendPingAsync(IPAddress address, int timeout);
public Task<PingReply> SendPingAsync(string hostNameOrAddress, int timeout);
public Task<PingReply> SendPingAsync(IPAddress address, int timeout, byte[] buffer);
public Task<PingReply> SendPingAsync(string hostNameOrAddress, int timeout, byte[] buffer);
public Task<PingReply> SendPingAsync(IPAddress address, int timeout, byte[] buffer, PingOptions options);
public Task<PingReply> SendPingAsync(string hostNameOrAddress, int timeout, byte[] buffer, PingOptions options);
}
public abstract class TcpStatistics {
public abstract long MaximumTransmissionTimeout { get; }
public abstract long MinimumTransmissionTimeout { get; }
}
}
namespace System.Net.Sockets {
public class LingerOption {
public LingerOption(bool enable, int seconds);
public int LingerTime { get; set; }
}
public class NetworkStream : Stream {
public void Close(int timeout);
}
public class Socket {
public void Close(int timeout);
public bool Poll(int microSeconds, SelectMode mode);
public int ReceiveTimeout { get; set; }
public int SendTimeout { get; set; }
public static void Select(IList checkRead, IList checkWrite, IList checkError, int microSeconds);
}
public class TcpClient {
public int ReceiveTimeout { get; set; }
public int SendTimeout { get; set; }
}
}
namespace System.Security.Cryptography.Pkcs {
public sealed class Rfc3161TimestampTokenInfo {
public Rfc3161TimestampTokenInfo(Oid policyId, Oid hashAlgorithmId, ReadOnlyMemory<byte> messageHash, ReadOnlyMemory<byte> serialNumber, DateTimeOffset timestamp, long? accuracyInMicroseconds, bool isOrdering, ReadOnlyMemory<byte>? nonce, ReadOnlyMemory<byte>? timestampAuthorityName, X509ExtensionCollection extensions)
public long? AccuracyInMicroseconds { get; }
}
}
namespace System.ServiceProcess {
public class ServiceBase {
public void RequestAdditionalTime(int milliseconds);
}
}
namespace System.Threading {
public class SynchronizationContext {
public virtual int Wait(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout);
protected static int WaitHelper(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout);
}
}
namespace System.Timers {
public class Timer {
public Timer(double interval);
public double Interval { get; set; }
}
}
There isn't a good, uniform way to make all of these members better. I've split this proposal into 3 parts: APIs which I think are fine, APIs which I can't figure out how to make better, and APIs that deal in seconds (which may not be good to add such APIs to).
APIs I'm fine with:
namespace System {
public static class GC {
+ public static GCNotificationStatus WaitForFullGCApproach(TimeSpan timeout);
+ public static GCNotificationStatus WaitForFullGCComplete(TimeSpan timeout);
}
}
namespace System.ComponentModel.DataAnnotations {
public class RegularExpressionAttribute {
! This one might not end up being that useful, since people generally don't ever manipulate an instance of this attribute.
+ public TimeSpan MatchTimeout { get; }
}
}
namespace System.Diagnostics {
public class Process {
+ public bool WaitForExit(TimeSpan timeout);
+ public bool WaitForInputIdle(TimeSpan timeout);
}
}
namespace System.IO {
public class FileSystemWatcher {
+ public WaitForChangedResult WaitForChanged(WatcherChangeTypes changeType, TimeSpan timeout);
}
public sealed class NamedPipeClientStream : PipeStream {
+ public void Connect(TimeSpan timeout);
+ public Task ConnectAsync(TimeSpan timeout, CancellationToken cancellationToken = default);
}
}
namespace System.Net.NetworkInformation {
public class Ping {
+ public PingReply Send(IPAddress address, TimeSpan timeout, byte[]? buffer = null, PingOptions? options = null);
+ public PingReply Send(string hostNameOrAddress, TimeSpan timeout, byte[]? buffer = null, PingOptions? options = null);
! Skipped EAP based methods; if they are desired, they can be added back in
! I added CancellationToken because it is probably worth it. If you don't want it you can remove it.
+ public Task<PingReply> SendPingAsync(IPAddress address, TimeSpan timeout, byte[]? buffer = null, PingOptions? options = null, CancellationToken cancellationToken = default);
+ public Task<PingReply> SendPingAsync(string hostNameOrAddress, TimeSpan timeout, byte[]? buffer = null, PingOptions? options = null, CancellationToken cancellationToken = default);
}
}
namespace System.Net.Sockets {
public class NetworkStream : Stream {
+ public void Close(TimeSpan timeout);
}
public class Socket {
+ public bool Poll(TimeSpan timeout, SelectMode mode);
+ public static void Select(IList checkRead, IList checkWrite, IList checkError, TimeSpan timeout);
}
}
namespace System.ServiceProcess {
public class ServiceBase {
+ public void RequestAdditionalTime(TimeSpan time);
}
}
namespace System.Threading.Tasks {
public class Task {
+ public bool Wait(TimeSpan timeout, CancellationToken cancellationToken);
}
}
namespace System.Timers {
public class Timer {
+ public Timer(TimeSpan interval);
}
}
APIs that I don't know how to make better:
Most of these are adding properties that would simply delegate to the existing millisecond-based properties. In the case of get-only properties, I went for adding extension methods. I am not sure how these could be made better; maybe it's not worth adding them at all.
namespace System.IO {
public abstract class Stream {
+ public TimeSpan ReadTimeoutTimeSpan { get; set; }
+ public TimeSpan WriteTimeoutTimeSpan { get; set; }
}
}
namespace System.IO.Ports {
public class SerialPort {
+ public TimeSpan ReadTimeoutTimeSpan { get; set; }
+ public TimeSpan WriteTimeoutTimeSpan { get; set; }
}
}
namespace System.Media {
public class SoundPlayer {
+ public TimeSpan LoadTimeoutTimeSpan { get; set; }
}
}
namespace System.Net.NetworkInformation {
+ public static class NetworkInformationTimeSpanExtensions {
+ public static TimeSpan GetPacketReassemblyTimeout(this IPGlobalStatistics statistics);
+ public static TimeSpan GetMaximumTransmissionTimeout(this TcpStatistics statistics);
+ public static TimeSpan GetMinimumTransmissionTimeout(this TcpStatistics statistics);
+ }
}
namespace System.Net.Sockets {
public class Socket {
+ public TimeSpan ReceiveTimeoutTimeSpan { get; set; }
+ public TimeSpan ReceiveTimeoutTimeSpan { get; set; }
}
public class TcpClient {
+ public TimeSpan ReceiveTimeoutTimeSpan { get; set; }
+ public TimeSpan SendTimeoutTimeSpan { get; set; }
}
}
namespace System.Timers {
public class Timer {
+ public TimeSpan IntervalTimeSpan { get; set; }
}
}
APIs which use seconds:
These suffer from the same problem as the previous group, but also only deal with seconds, meaning that most TimeSpan values would need to be rounded out.
namespace System.Data {
! This would need to be a DIM.
public interface IDbCommand {
+ TimeSpan CommandTimeoutTimeSpan { get; set; }
}
+ public static class DataTimeSpanExtensions {
+ public static TimeSpan GetConnectionTimeout(this IDbConnection connection);
+ }
}
namespace System.Data.Sql {
public sealed class SqlNotificationRequest {
+ public TimeSpan TimeoutTimeSpan { get; set; }
}
}
namespace System.Data.SqlClient {
public sealed class SqlBulkCopy {
+ public TimeSpan BulkCopyTimeoutTimeSpan { get; set; }
}
public sealed class SqlConnectionStringBuilder {
+ public TimeSpan ConnectTimeoutTimeSpan { get; set; }
+ public TimeSpan LoadBalanceTimeoutTimeSpan { get; set; }
}
public sealed class SqlDependency {
+ public SqlDependency(SqlCommand command, string options, TimeSpan timeout);
}
}
namespace System.Net.Sockets {
public class LingerOption {
+ public LingerOption(bool enable, TimeSpan time);
+ public TimeSpan LingerTimeSpan { get; set; }
}
public class Socket {
+ public void Close(TimeSpan timeout);
}
}
I omitted *WebRequest/ServicePoint/SmtpClient since we aren't innovating in that space. Additionally, I omitted the SynchronizationContext API because the docs mention a CLS-compliant alternative API which does take a TimeSpan.
@reflectronic thanks for providing the list, that is very handy.
@terrajobst regarding your comment:
There is a lot of trickery due to .NET Framework compat and how we type forward contracts.
Is there anything impeading us adding these overloads just in netcoreapp? In case that's fine, then I guess this issue might be ready to take to API Review.
LOL. No idea what I meant. It was a different time back then. Maybe I thought how we can OOB these methods? Who knows. Let鈥檚 just look at the APIs in one of our next reviews. Seems generally like a fine feature.
sounds good, will mark it as ready for review then.
Er... there wasn't actually an API proposal before. However, I have now updated my original comment with an API proposal.
@reflectronic It seems you are proposing adding parallel properties. So both an int version and a TimeSpan version. What behavior would result from reading and writing both properties? When I set 1.5 seconds using the TimeSpan version, what do I get reading the int version? Probably a rounded result.
Thanks to default interface methods it might actually be feasible to modify the interfaces.
I think it's quite problematic from an API design standpoint to have both versions. An alternative would be to use TimeSpan for any new timeout APIs but keep existing ones using integers.
Another alternative is to really put TimeSpan versions everywhere (in parallel to the old ones), deprecate the old ones, create a Roslyn based automatic migration tool (this should just work for 99% of cases) and delete the int versions in the next major version. This would be really awesome if this was feasible.
Using int for a time span in the first place is a pretty obvious design bug stemming from .NET 1.0. It would be awesome if we could totally get rid of that.
The concept of having an automatic API upgrade tool (based on Roslyn) should be explored more. Not just for this situation. Maybe .NET 5 is the right time to pull the trigger on some cleanups like this one.
@GSPP What would you envision such an upgrade tool to do? Would Roslyn analyzers with code fixes that allow people to migrate from the old overloads to the modern TimeSpan variants be feasible or do you think an API migration requires a more "rigid" approach?
Could be interesting to explore this path and see just how feasible providing (semi-)automated migrations from older APIs would be.
@aevitas I'm not too familiar with the Roslyn analyzer infrastructure. These upgrades need to happen in mass, across the entire solution, with a preview and a report of the changed locations.
If the old APIs are to be dropped in .NET 5 then opening a pre-5 solution with .NET 5 would immediately result in a lot of compiler errors. The tool should then go and fix them.
Maybe .NET 5 needs to keep all old APIs and delete them in .NET 6.
In the long term, I think it is very undesirable to keep both versions. Either the old version must eventually be deleted, or the change not taken at all, IMHO.
int is replaced with TimeSpan.namespace System {
public static class GC {
+ public static GCNotificationStatus WaitForFullGCApproach(TimeSpan timeout);
+ public static GCNotificationStatus WaitForFullGCComplete(TimeSpan timeout);
}
}
namespace System.ComponentModel.DataAnnotations {
public class RegularExpressionAttribute {
! This one might not end up being that useful, since people generally don't ever manipulate an instance of this attribute.
+ public TimeSpan MatchTimeout { get; }
}
}
namespace System.Diagnostics {
public class Process {
+ public bool WaitForExit(TimeSpan timeout);
+ public bool WaitForInputIdle(TimeSpan timeout);
}
}
namespace System.IO {
public class FileSystemWatcher {
+ public WaitForChangedResult WaitForChanged(WatcherChangeTypes changeType, TimeSpan timeout);
}
public sealed class NamedPipeClientStream : PipeStream {
+ public void Connect(TimeSpan timeout);
+ public Task ConnectAsync(TimeSpan timeout, CancellationToken cancellationToken = default);
}
}
namespace System.Net.NetworkInformation {
public class Ping {
+ public PingReply Send(IPAddress address, TimeSpan timeout, byte[]? buffer = null, PingOptions? options = null);
+ public PingReply Send(string hostNameOrAddress, TimeSpan timeout, byte[]? buffer = null, PingOptions? options = null);
! Skipped EAP based methods; if they are desired, they can be added back in
! I added CancellationToken because it is probably worth it. If you don't want it you can remove it.
+ public Task<PingReply> SendPingAsync(IPAddress address, TimeSpan timeout, byte[]? buffer = null, PingOptions? options = null, CancellationToken cancellationToken = default);
+ public Task<PingReply> SendPingAsync(string hostNameOrAddress, TimeSpan timeout, byte[]? buffer = null, PingOptions? options = null, CancellationToken cancellationToken = default);
}
}
namespace System.Net.Sockets {
public class NetworkStream : Stream {
+ public void Close(TimeSpan timeout);
}
public class Socket {
+ public bool Poll(TimeSpan timeout, SelectMode mode);
+ public static void Select(IList checkRead, IList checkWrite, IList checkError, TimeSpan timeout);
}
}
namespace System.ServiceProcess {
public class ServiceBase {
+ public void RequestAdditionalTime(TimeSpan time);
}
}
namespace System.Threading.Tasks {
public class Task {
+ public bool Wait(TimeSpan timeout, CancellationToken cancellationToken);
}
}
namespace System.Timers {
public class Timer {
+ public Timer(TimeSpan interval);
}
}
Might recommend nixing System.Timers from the list. That's largely a dead API surface.
Not essential to ship 5.0
Open question:
Some apis use -1 for special meaning (infinity). How to represent it in TimeSpan?
For input cases, we can just disable the infinity semantic. But for output (namely RegularExpressionAttribute.MatchTimeout), I can't find a suitable representation.
Well, there is Timeout.InfinityTimeSpan.
Contribution pain:
The implementation for these apis are simple enough, but the major pain should be documentation, since the policy that new api must have xml doc in source. Some of these don't have xmldoc, some have outdated ones.
Options for contribution:
int overloads.@huoyaoyuan I think that you can just add new methods with xml docs and don't worry about the existing methods that don't have the xml docs, as they are going to be added as part of separate work. Please let me know if you need any help.
Most helpful comment
Okay, I have semi-automatically generated a list of types (and the specific members) that don't use a
TimeSpanwhere it would be appropriate:Offending Types
There isn't a good, uniform way to make all of these members better. I've split this proposal into 3 parts: APIs which I think are fine, APIs which I can't figure out how to make better, and APIs that deal in seconds (which may not be good to add such APIs to).
APIs I'm fine with:
APIs that I don't know how to make better:
Most of these are adding properties that would simply delegate to the existing millisecond-based properties. In the case of get-only properties, I went for adding extension methods. I am not sure how these could be made better; maybe it's not worth adding them at all.
APIs which use seconds:
These suffer from the same problem as the previous group, but also only deal with seconds, meaning that most TimeSpan values would need to be rounded out.
I omitted
*WebRequest/ServicePoint/SmtpClientsince we aren't innovating in that space. Additionally, I omitted theSynchronizationContextAPI because the docs mention a CLS-compliant alternative API which does take aTimeSpan.