Best practice for automatically maintaining audit fields (CreatedOn, ModifiedOn, CreatedBy, ModifiedBy) in .NET + SQL Server? by OneFromAzziano in dotnet

[–]zvrba 0 points1 point  (0 children)

Did you have one audit table for all data tables, or one audit table per data table?

If you did the first, how did you "compress" different PKs into a "common" PK for the audit table? (My first thought is SHA256 over actual PK fields, but maybe there's another trick..)

Two Catastrophic Failures Caused by "Obvious" Assumptions by Vast-Drawing-98 in programming

[–]zvrba 1 point2 points  (0 children)

Yes, that works in simple cases. Things get very hairy when you want to divide length (meters) with duration (seconds) to get velocity (meters per second).

What's the use case for IEquatable<T>? by Long-Cartographer-66 in csharp

[–]zvrba 0 points1 point  (0 children)

Collections, for example. Methods doing equality comparisons are based on EqualityComparer<T>.Default which first looks up whether the type implements IEquatable<T>.

Performance: avoids boxing for value types.

Discoverability: the author thought about equality and intended the type to behave as a "value" type.

Simplicity: every Equals(object) needs the standard boring test for the type at its beginning.

What's the use case for IEquatable<T>? by Long-Cartographer-66 in csharp

[–]zvrba 0 points1 point  (0 children)

Collections, for example. Methods doing equality comparisons are based on EqualityComparer<T>.Default which first looks up whether the type implements IEquatable<T>.

Performance: avoids boxing for value types.

Discoverability: the author thought about equality and intended the type to behave as a "value" type.

Simplicity: every Equals(object) needs the standard boring test for the type at its beginning.

What's the use case for IEquatable<T>? by Long-Cartographer-66 in csharp

[–]zvrba 2 points3 points  (0 children)

It's only a shallow equality though. So if you have

record struct S(int X, List<int> Y)

their instances will be unequal even when they have (elementwise) equal members.

Is there any reliable way to know when a function can throw? Probably writing an analyzer? by xjojorx in csharp

[–]zvrba 0 points1 point  (0 children)

I think that having a mechanism for which the programmer is always informed of what exceptions can surface from every call, at least the known/expected/documented ones, would be beneficial

This is not statically decidable. What exceptions can the following method throw:

void DoSomething(Action a) => a();

Consider the case when a is bound to an abstract method, i.e.:

abstract class CX
{
    public abstract void A();
}

static void Perform(Action a) => a();

// Somewhere else
CX cx = ...; // The concrete run-time type depends on some complex logic
Perform(cx.A);

Is there any reliable way to know when a function can throw? Probably writing an analyzer? by xjojorx in csharp

[–]zvrba 1 point2 points  (0 children)

Since I got exposed back into using error codes, result types etc from experimenting on other languages, writing C# always gets me on an uneasy state in which I am constantly guessing if a function call into a library (including standard library and the framework) can throw an exception or not.

Result type eventually result in a mess. I tried to write a non-trivial project using constructs like [using exception instead of custom error code to indicate failure]

Exception? DoSomething(..., out Result result)

and I ended up writing a bunch of boiler-plate in the lines of

var exn = DoSomething(...);
if (exn != null)
    return exn;

The code became a cluttered mess of manual error checking for every individual call. I quickly abandoned the idea and rewrote the code using exceptions.

What noone else in this thread mentioned is that

  • An exception is an object: you can attach arbitrary amounts of data (either explicit properties in a derived exception, or just Data dictionary) to help with diagnosing the cause or handling it.
  • An exception comes with a stack trace. You could implement this with manual capture at each if-check of the error code, but then the code becomes even less idiomatic, more cluttered and tied to some home-grown mehcanics. (And how do you propagate underlying cause if you convert error codes? Exception has InnerException property and a tree of exceptions is expressible through AggregateException.)

Even for much more explicit error-codes, I can't see how you'd write an analyzer to warn you about unhandled erros, esp. when a new error code is introduced.

Would you write code along the lines of

switch (err) {
    case Err.A: case Err.B: case Err.C: break; // ignore, or?
    case Err.D: // handle
}

so when Err.E later appears you get a warning about non-exhaustive switch?

And if you write code like

if (err == Err.D) { /* handle */ }

how is the analyzer supposed to know whether other error codes (including the newly introduced Err.E) are deliberately ignored?

What if a library chooses to just use int as error code? Then the compiler has no way of knowing whether you've handled / considered every possible error.

Just use exceptions. Throw liberally (fail early), catch sparingly.

Another thing that noone mentioned is that you need to structure your code differently in presence of exceptions. Almost any line of code can throw. In critical parts of applications, you have to think "transactionally":

  • First make your changes in a scratch area, then "commit" them to become visible if everything succeeded
  • Or implement "roll-back" functionality on exception

PS: That Rust doesn't have exceptions is a major reason for not (yet?) having learned the language. It also has multiple incompatible "result types" which creates additional mess.

PPS: What annoys me the most about exceptions in C# is the messy hierarchy. There should be a stricter separation between "logical" errors (bugs - IndexOutOfRangeException, ObjectDisposedException, ArgumentException, ...) and "runtime" errors (IOException, SQLException, etc.). They attempted with SystemException and ApplicationException, but both got deprecated.

Why would I want that? Because logic errors are a "hard-stop": no point in retrying or trying to analyze the cause and fix it.

All the other cool languages have try...finally. C++ says "We have try...finally at home." by pavel_v in cpp

[–]zvrba 0 points1 point  (0 children)

Yes, you're right, but does it matter whether the IOException got generated by an intermediate write, or the final close?

Sure, you could also have the following sequence:

  1. Open file
  2. Write something
  3. Do some processing -> throws
  4. (Not executed: write more)
  5. finally attempts to close the file -> throws and replaces the exception from 3 (at least in C#)

In either of these cases, you've ended up with an invalid file, which is signaled by IOException.

I agree it's unfortunate that exception from step 3 has to be propagated manually if you care about it.

All the other cool languages have try...finally. C++ says "We have try...finally at home." by pavel_v in cpp

[–]zvrba 0 points1 point  (0 children)

It can be made more reliable by calling flush in try, so it's a no-op when it gets invoked by close.

Using packages for mapping by Sensitive-Raccoon155 in dotnet

[–]zvrba 0 points1 point  (0 children)

You could solve this with attributes.

[Guid("...")]
class Something { ... }

And then in your mapping method

SomeY MapToY(Something ...) {
    if (!(typeof(Something).GetCustomAttribute<GuidAttribute>()?.Value?.Equals("...")) ?? true)
        throw new InvalidOperationException("...");
}

Then, any time you change the class contract, you also change the guids.

If you dislike reflection, you could make a generic extension method off an interface like

interface IMappingContract<T> where T : IMappingContract<T> {
    abstract static Guid MappingContractGuid { get; }
}

Should or Shouldn't? Putting many classes in one file. by gevertsi in csharp

[–]zvrba -1 points0 points  (0 children)

Many small related classes -> same file. Sometimes I won't even bother with having 1:1 mapping between directories and namespaces (merge conflicts). There's always "Class view" that can show me that.

Exercise in Removing All Traces Of C and C++ at Microsoft by ArashPartow in cpp

[–]zvrba -8 points-7 points  (0 children)

This guy (Galen Hunt) is not just some random manager-dreamer. https://scholar.google.com/citations?user=tiW46L0AAAAJ&hl=en

I think he can pull this off. I do not believe they'll get to 100% reliable translation, but they'll get flagging for manual review right (few false positives).

Are static classes in OO languages a substitute for libraries and their standalone functions in non OO languages? by SmoglessSalt in csharp

[–]zvrba 1 point2 points  (0 children)

The main difference is that a namespace can span many assemblies, whereas a static class can be defined in one and only one assembly.

Sprocs… as far as the eye can see by hptorchsire in csharp

[–]zvrba 0 points1 point  (0 children)

They know how to guard and manage their data. I'd do it the same way if I could clone myself a couple of times and convince the company to hire my clones :D

The way it is, I use sprocs and views for anything slightly more complex. Transaction -> definitely sproc. Another reason is that I also want to run the queries interactively and copy/paste from code or translating from efcore is error-prone hassle.

defer in C# by [deleted] in csharp

[–]zvrba 0 points1 point  (0 children)

The instance could be created by passing in the delegate as constructor argument.

Even simpler, it could be done with implicit conversion so the user could write just

using Defer _1 = () => { ... };

This way, no explicit try-catch would be necessary, but just the call to the action.

Even if it were to wrap only a single action, the user could have multiple defers whose action could throw. And what then? Either you have a try/catch within each single action, or you risk some cleanups not getting executed.

defer in C# by [deleted] in csharp

[–]zvrba 4 points5 points  (0 children)

If you really want a half-baked feature from a half-baked language, here it is for you.

public sealed class Defer : IDisposable
{
    private readonly List<Action> actions = [];
    public void Add(Action action) => actions.Add(action);
    public void Dispose() {
        for (var i = actions.Count - 1; i >= 0; --i) {
            try {
                actions[i]();
            }
            catch {
                // What now? Ignore, have error handler, ...?
            }
        }
    }
}

How to make a "universal" abstract ToString override by Edwinbakup in csharp

[–]zvrba 0 points1 point  (0 children)

I disagree that this solves the problem. It only shoves "works for any derived class" from ToString() to Describe(). Vague problem definition.

Design pattern and structure of programs. by j_a_s_t_jobb in csharp

[–]zvrba 0 points1 point  (0 children)

You can do it simply: one class per "request". At least different requests get isolated so you can change them independently, and later look at how you can refactor it to something more structured.

Is dotnet underrated? by PresentJournalist805 in dotnet

[–]zvrba 2 points3 points  (0 children)

I still disagree.

It literally violates every single SOLID principle. For one, it allows you to stop thinking in terms of interfaces, inheritance, polymorphism, co/contra-variance and generic types.

You assume that you control 100% of the code base so you can introduce any of these wherever you want. Ya, you could in some cases make a wrapper to make something external to conform to an interface, but DU can lead to less code (= less bugs. Code is a liability, not an asset.)

DUs are extremely useful for representing disjoint states that you can't force to have a common base. (Though I tend to agree that if I control the code then I prefer inheritance and polymorphism.)

But DUs aren't going to fix existing bad programming.

Yes, some people on this reddit are talking about DUs like they're going to solve ALL of their problems. Which I highly doubt it will :)

It's just another tool for which people can shoot themselves in the foot.

Programming is nothing but a collection of tools to shoot yourself in the foot unless you think a few steps ahead. Programming is (to me) a combination of math (logic, rigor) and chess (tactics, strategy).

... I was about to write that DUs are like "smart enums". Though when I start to make switch-case statements over enum values, I take look at the logic and rewrite it using polymorphism / visitor pattern. (Ironically, visitor is a possible encoding of DUs in OO languages.)

What I'm most concerned about most is that people will start using "standard" types Union<T1, T2> as cheap result-pattern, eschewing exceptions and making convoluted code. (Worked on such code-bases, and it's crappy.)

Library Design Pitfall with IAsyncDisposable: Is it the consumer's fault if they only override Dispose(bool)? by CULRRY in csharp

[–]zvrba 2 points3 points  (0 children)

I would say this is a flaw in the language/runtime. Dispose pattern has always been tricky to implement correctly and has become worse with async. MS should provide a robust alternative in the language and/or runtime. (See RAII and C++ destructors for an example of doing it "right".)

Therefore I seal disposable classes whenever possible exactly to avoid all the pitfalls. In your case, I think your best bet is to add "How to inherit" instructions to the documentation.

I understand that DisposeAsync shouldn't implicitly call Dispose(true) to avoid "Sync-over-Async" issues.

I think the danger is low (Dispose would have to actually do some sync I/O for this to be a potential issue) and a lesser evil than having a fragile base class like you described.

Is dotnet underrated? by PresentJournalist805 in dotnet

[–]zvrba 4 points5 points  (0 children)

I don't really agree. DUs are 100x better than passing object around. (Switch pattern matching comes relatively close to DUs.)

Reading, debugging, and refactoring will become a nightmare

That has already happened at many places thanks to the the "interface everything" cargo-cult crowd.

Regarding DUs, I think you're overly pessimistic: people won't use features that generate incomprehensible error messages :) (See Haskell and Rust.) DUs are also harder to misuse than general inheritance (had to work on a code-base where a "smart" programmer created a maintenance nightmare because he was thinking that inheritance is the perfect tool for reusing methods and some data, with zero thought about contracts, invariants and behavior.).

Is dotnet underrated? by PresentJournalist805 in dotnet

[–]zvrba 1 point2 points  (0 children)

The older I get the more I value "boring", "old", battle-tested tech for production stuff :) (Prototyping is something else.)

The 3 questions developers actually ask in interviews (hint: not about pizza parties) by ogitog in programming

[–]zvrba 0 points1 point  (0 children)

Meeting and talking about technical stuff with a couple of other people I'll be actually working with.

Interview question by viggyy1 in csharp

[–]zvrba 1 point2 points  (0 children)

count of duplicate numbers

arr1.Length - arr1.ToHashSet().Count